ISR fallback and App Shell upgrade

generateStaticParams here returns only two of the four regions, and only one station within each. Everything else is an unlisted param — which is the interesting case.

For an unlisted URL, Next.js serves the route’s App Shell immediately, streams the real content, and then re-renders the page in the background with the now-known params. The next visitor to that same URL gets the upgraded result from cache. This is the Cache Components equivalent of fallback: true.

What to check

Listed. Click a prerendered link. Fully rendered, no fallback, stamps dated to the build.

Unlisted, first visit. Click an unlisted link. The region header and station panel arrive as fallbacks first, then stream in.

Unlisted, second visit. Go back and click the same link again. Now it comes from the background upgrade — no fallback. This is the whole mechanism in one click.

Count the fallbacks instead of squinting:

# both params known — no fallbacks, ever
curl -s localhost:3000/isr-fallback/europe/london | grep -o 'data-fallback="[^"]*"' | sort -u

# known region, unlisted station — PARTIAL shell, station only
curl -s localhost:3000/isr-fallback/europe/paris | grep -o 'data-fallback="[^"]*"' | sort -u

# neither known — generic shell, both stream
curl -s localhost:3000/isr-fallback/africa/nairobi | grep -o 'data-fallback="[^"]*"' | sort -u

# then run either unlisted one again: no fallbacks, it upgraded

Prefetching counts as the first visit. In a build, merely scrolling an unlisted link into view starts the upgrade before you click, so the click often lands on the upgraded result. To see the cold path, use curl or type the URL.

Prerendered at build time

These param values came from generateStaticParams (europe, asia-pacific), so they are complete static pages.

Not prerendered

No build-time params. First visit gets the App Shell; the upgrade happens behind it.

Deep links — the partial shell

Within a prerendered region, only the first station is prerendered. The rest reach the station page with an unlisted [station] param but a known [region] — so the shell they get already has the region header filled in, and only the station streams. That partial shell is the detail worth seeing.