A production-readiness checklist for vibe-coded Next.js apps
Eleven checks that catch the defects automated scans and code review miss, each one drawn from a real failure in a Next.js application that already passed its tests.
Every item below comes from a defect that survived a passing test suite, a green build, and a person reading the diff. None of them were exotic. They survived because nothing looked for them.
Run these against a production build, served, in a browser. Most of them are invisible in development.
Status codes, not just content
Ask for a URL that should not exist and read the status line, not the page.
A page can render a perfectly good "not found" screen and still answer 200. In
one case a loading boundary on a shared route segment meant the framework sent
a streamed shell before the not-found decision resolved, so every route beneath
it answered 200 on its not-found path. Without JavaScript the page never
resolved at all.
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/en/does-not-exist
Also check what an unmatched address under a localized prefix actually renders. If it falls through to the framework's built-in error page you lose the document language, the main landmark, your translations, and your styling all at once.
Forced colors, in a browser
Windows high-contrast mode replaces your colours with system colours. A filled button whose label resolves to the same system colour as its own background becomes an empty box.
This one is worth dwelling on: an automated accessibility scan reported zero violations on a page where every primary action was unreadable. The computed values looked correct — white on dark blue. The failure was a rendering effect, because browsers paint a backplate behind text over a background image, and the label was the same colour as the plate.
Filled controls need an explicit system-colour pair. Scans cannot see this. Look at it.
Target sizes, at the widest viewport
Check pointer target sizes on desktop, not only on mobile. It is common to add a comfortable tap target inside a small-screen media query and leave the wide layout at the natural line height of the text. Navigation links seventeen pixels tall pass every automated check and fail the minimum target size.
Reflow, zoom, and text spacing
Three separate checks that people collapse into one:
- narrow reflow at 320 CSS pixels, with no sideways scrolling;
- a 400 per cent zoom equivalent, which is a 320 by 256 viewport;
- increased line height, letter spacing, word spacing, and paragraph spacing applied as user styles.
Fluid spacing that looks elegant can still push a container past the viewport at one of the three.
Reduced motion at the token, not the component
If durations come from a custom property, override the property once under the
reduced-motion query and every transition inherits it. Scattering per-component
overrides guarantees one gets missed. Verify by reading a computed
transition-duration with the preference active, not by reading the stylesheet.
Discovery signals that agree with each other
Your indexing directives, your robots.txt, and your sitemap should be three
views of one decision. Common contradictions:
- a sitemap listing a URL whose page answers
noindex; robots.txtadvertising a sitemap that declares no URLs;- a canonical URL on a page you asked search engines to drop;
- disallowing a URL in
robots.txtthat carries anoindexdirective, so the crawler can never read the directive that would remove it.
Derive all three from one list. If they can disagree, eventually they will.
Alternates that resolve
If you publish more than one language, check that every declared language
alternate returns 200. A common shape: content exists in one language, the
alternates are generated from the full set of supported languages, and the site
advertises addresses that answer 404.
The rule that avoids it: an alternate is a published sibling, not a supported locale.
Preview deployments that cannot compete
A preview deployment with production canonical URLs competes with production in search results. Gate discovery output on an explicit production signal, and check that a preview emits no canonical, no alternates, and an empty or absent sitemap.
Validators that report instead of throwing
Two failure modes worth testing directly.
First, dates. new Date("2026-02-31") does not fail — it rolls into March. A
format check plus a parse check still admits dates that do not exist. Round-trip
the value instead.
Second, order of operations. A refinement that calls toISOString on an invalid
date throws, and a validator that throws reports nothing at all: the caller sees
a crash rather than a field-level message. Guard validity first.
The artifact you ship, not the repository you build
If you distribute a sanitized copy of your source, verify the copy. A repository can be perfectly healthy while the artifact derived from it is broken: files that survive removal while importing modules that did not, helpers left with no consumer, generated files whose trailing byte differs from what the compiler emits.
Build the artifact, install it, and run its own verification command. Three separate customer-breaking defects reached a release before this check existed.
Tests that can actually fail
Before trusting a new check, break the thing it protects and watch it fail.
A forced-colors test that applies the preference after rendering can pass against ordinary colours and prove nothing. A test asserting a class name rather than a computed style passes for a focus ring that a later rule overrides. Both were in this list until someone tried to break them.
If a check has never been observed failing, it is a comment, not a test.