Build1 distinct publisher3 min readUpdated
A fresh app with one plain dynamic route failed next build until the param read was wrapped in Suspense. Budget refactoring time, not two lines of config.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
Someone scaffolded a fresh `create-next-app` on Next.js 16.3.1, set `cacheComponents: true` and `partialPrefetching: true` in `next.config.ts`, and added the most boring dynamic route available: a product page that awaits its own `params` [1][2]. `next build` failed outright, exit code 1 [3][6]. That is the part of the release worth planning around, because it means the flag is a contract change enforced at build time rather than an optimisation you switch on when convenient.
The failing page had no database, no external API, and not even a real `fetch` [2]. Its data function awaited a 300ms `setTimeout` and returned a hardcoded object [2]. The error names the tripwires: `fetch(...)`, `cookies()`, `headers()`, `params`, `searchParams` or `connection()` accessed outside of `<Suspense>` prevents the route from being prerendered [4]. Since the only request-time input in that file was the route param, the gate fires on reading `params` alone, with no I/O involved [12].
The error offers three ways out, and they are not equivalent: stream, by wrapping the data access in `<Suspense fallback={...}>`; cache, by marking the access `"use cache"`, which the message says does not apply to `connection()`; or block, by exporting `const instant = false` to allow a blocking route [5]. The cache route is described as being for uncached data such as `fetch` and database calls, which leaves streaming or blocking as the practical answers for a page whose dynamic input is a URL segment [13].
Streaming worked, but not as an annotation. The param-awaiting logic had to move out of the page component into a child, `ProductDetails`, with the page rendering it inside `<Suspense fallback={<p>Loading product...</p>}>` [7]. That is one file becoming two components plus a boundary: a code change, not a config change [14]. The build then succeeded and reported the route as a partial prerender, static HTML with dynamic server-streamed content [8]. Everything outside the Suspense boundary, the shell, was prerendered; the product-specific part streams behind it [9].
That shell is also the dependency chain worth noting. According to the dev.to writeup, Partial Prefetching, the client-side navigation feature 16.3 adds, is described as a reusable shell per route cached on the client, and it depends on the same shell existing in the first place [10]. So the two flags are not independent purchases. The author's conclusion is that with Cache Components on, every uncached or runtime access found during prerendering needs an answer the moment you flip the switch, and that this is a larger adoption cost than "add two lines to your config" suggests [11].
Two things to watch on your own codebase. First, the count: run the flag on a branch and total the routes that fail, because that number is your refactoring backlog, and every one of them is a decision about whether users see a shell or wait. Second, the temptation of `instant = false`, which will clear a build quickly [5]. A route allowed to block is not the partial-prerender output the build reported for the streamed version, so mass-applying the escape hatch appears to trade away the prerendered shell that partial prefetching is built on [15]. That is a defensible choice per route. It is a bad default applied by find-and-replace.
Follow any of these and your For You feed starts watching them — no settings page required.
Ranked by verification strength, evidence, and original report placement.
The author concludes that on a 16.3 app with Cache Components on, every uncached or runtime access found during prerendering needs an answer (stream, cache, or block) the moment the flag is turned on, that adoption is not gradual but a build gate, and that this is a bigger adoption cost than "add two lines to your config" implies.
A developer scaffolded a fresh create-next-app project on Next.js 16.3.1 and set cacheComponents: true and partialPrefetching: true in next.config.ts.
The test route was /products/[id]: an async page component that awaited params and called a local getProduct function which awaited a 300ms setTimeout and returned a hardcoded object { id, name, price: 42 }. There was no database, no external API, and no real fetch.
next build failed with: Error: Route "/products/[id]": Next.js encountered uncached or runtime data during prerendering.
The error states that fetch(...), cookies(), headers(), params, searchParams, or connection() accessed outside of <Suspense> prevents the route from being prerendered, blocking the page load and leading to a slower user experience.
The error lists three fixes: [stream] provide a placeholder with <Suspense fallback={...}> around the data access; [cache] for uncached data (fetch, database calls) cache the access with "use cache" (does not apply to connection()); [block] set export const instant = false to allow a blocking route.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Reproducible first-hand test, single publisher
The core mechanism is documented at an unusually verifiable level for a single-source story: exact version (16.3.1), the config flags, the failing page source, verbatim error text with its three remedies, the refactored code, and the resulting route table with its legend. That supports the build-gate and partial-prerender claims directly. It stops short of high confidence because there is one publisher, no vendor documentation or release notes in the cluster to confirm intended behaviour, and the broader conclusions about real applications are extrapolated from a five-file project.
Feature shipped; one test project, no production reports
The framework capability itself is released and usable (16.3.1 flags plus a first-party adoption Skill), which is real adoption surface. Actual usage evidence in the cluster is a single developer's scaffolded five-file project migrated in two minutes, with the author explicitly asking whether anyone has run the migration on a production route tree. No teams, deployments, or scale data are supplied, so measured adoption stays low.
Narrow claim solid, generalisation runs ahead of evidence
The article is deflationary rather than promotional — it argues the upgrade is undersold as 'two lines of config' — and its mechanical claims match the shown build output almost exactly, so there is little inflation on the core. The modest positive gap comes from scope: the headline conclusion that Cache Components is a build gate with a significant adoption cost for real apps is asserted from a five-file scaffold, with no production route-tree data and no measurement of the runtime benefit the partial-prerender output is supposed to deliver.
Independent practitioner with mild self-promotion
The author has no disclosed vendor relationship and publishes a result that is unflattering to the framework's easy-upgrade framing, which argues against captured incentives. Countervailing pressures are visible though: a conflict-shaped headline on an engagement-driven developer platform, an explicit pointer to a longer breakdown on the author's own website, and an endorsement of the vendor's first-party adoption Skill without independent testing of it.
High confidence on mechanism, low on scale
Confidence is high that the described build failure and Suspense remedy behave as shown, because the artefacts are quoted end to end and independently reproducible by any reader on 16.3.1. Confidence is low on everything beyond that: single publisher, no vendor corroboration, no production migration evidence, and no measurement of the performance benefit or the cost of auditing an existing route tree.
build
Next.js's architecture is working. Its operating manual isn't keeping up.1 distinct publisher
build
A build step instead of a backend: 1,025 records, 8 locales, no runtime API1 distinct publisher
build
Tailwind v4 moves your tokens into CSS. The token name is now the API.1 distinct publisher
build
The MCP test that matters: a log tool that fetched the data and then said it failed1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 19, 2026