ISR with Cache Components: Prerendering a Subset of Routes

How Cache Components prerender the routes you list in generateStaticParams, serve an App Shell for the rest, and upgrade unknown URLs after their first visit.

8 min read

ISR with Cache Components prerenders the routes you list in generateStaticParams and serves an App Shell for everything else. Unknown URLs open instantly, then upgrade in the background once their params are known, so you do not pay build time for pages nobody visits.

This article assumes the Cache Components model. Enable it together with partial prefetching so links start the upgrade before the click:

typescripttypescript
// next.config.ts
import type { NextConfig } from 'next'
 
const nextConfig: NextConfig = {
  cacheComponents: true,
  partialPrefetching: true,
}
 
export default nextConfig

partialPrefetching makes a prefetched link render with the destination params resolved, so the first real navigation often lands on the upgraded page instead of the shell.

Choosing the subset

generateStaticParams returns the param values worth prerendering at build time. Popular pages ship fully static, while the long tail stays on demand.

App.tsxApp.tsx
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const res = await fetch('https://api.example.com/posts')
  const posts = await res.json()
  return posts.slice(0, 10).map((p: { slug: string }) => ({ slug: p.slug }))
}

Only these ten slugs become concrete pages during the build. Every other slug falls back to the shell and upgrades later. In the Pages Router this took getStaticPaths with fallback: true; under Cache Components that fallback behavior is the default, and generateStaticParams only decides which values skip it.

Build time versus runtime

The behavior depends on whether the params were listed.

URLBuild timeFirst visitLater visits
Listed paramsPrerendered pageStatic pageStatic page
Unknown paramsApp ShellShell, then streamed contentUpgraded page

For a listed slug, the visitor gets a fully static page with no server work. For an unlisted slug, the App Shell arrives instantly and the missing parts stream in, then the route upgrades in the background for the next visitor. A prefetch counts as that first visit, so a link that enters the viewport can start the upgrade before the click.

The split is visible in the next build route table, where the listed slugs sit under the dynamic segment row:

texttext
└   /blog/[slug]
  ├ ◐ /blog/[slug]
  ├ ○ /blog/shipping-faster
  └ ○ /blog/type-safe-routes

The row is the App Shell that serves every unlisted slug. The rows are the concrete pages built from generateStaticParams, prerendered in full because their params and their cached data were both known at build time. If a listed slug still shows , its data was not cached, so only the shell could be built.

What the upgrade produces

The background render tries to push the static boundary as far down the tree as it can. If every data access is cached and all params resolve, the upgrade produces a fully static page. If some part still reads uncached data or cookies, the upgrade produces a cached page that keeps those fallbacks streaming.

Params resolve in route order. A param value that generateStaticParams did not return stays unresolved and blocks deeper params from upgrading in the same pass. The reusable frame itself is the App Shell.

Choosing what to prerender

Not every route deserves build time. Each prerendered page costs build work and storage, so list the pages that benefit most from being ready and leave the rarely visited long tail on demand.

A page visited once between deploys spent its build time for nothing. For controlling whether unlisted params are allowed at all, see generateStaticParams and dynamicParams.

Rune AI

Rune AI

Key Insights

  • generateStaticParams picks the subset of routes to prerender.
  • Unknown URLs get an App Shell instantly, then upgrade in the background.
  • A prefetch counts as the first visit, starting the upgrade before the click.
  • The upgrade is static when all data is cached, otherwise it keeps fallbacks.
  • This replaces fallback: true from the Pages Router.
RunePowered by Rune AI

Frequently Asked Questions

Do I need partialPrefetching for ISR with Cache Components?

It is not required for the App Shell, but enabling it makes prefetched links start the background upgrade before the click, so navigation lands on the upgraded page.

Does every unknown URL get prerendered at build time?

No. Only the param values returned by generateStaticParams are prerendered. Everything else is upgraded on demand after its first visit.

Conclusion

ISR with Cache Components means you prerender the routes that matter and let everything else upgrade on demand. The App Shell gives unknown URLs an instant first paint, and the background upgrade makes the second visit static.