How to Fix: Page with ISR (generateStaticParams() and dynamicParams=true) is taking too long for not generated content
The slowdown is not really an ISR failure; it is the first-render path for uncached dynamic params getting serialized by how the app is deployed. When a route uses generateStaticParams() with dynamicParams = true, Next.js can prebuild some params and still allow others at runtime, but in Docker/Kubernetes setups the first request for a non-generated slug often becomes unexpectedly slow because the app must generate, cache, and persist that page on demand.
Understanding the Root Cause
In the App Router, a dynamic route such as /[slug] can combine generateStaticParams() and dynamicParams = true. That means:
- Known params returned by generateStaticParams() are built ahead of time.
- Unknown params are generated on the first request.
- After generation, the result is stored for future requests according to ISR rules.
On paper, this sounds ideal. In practice, the latency spike appears when the first request hits a slug that was not generated during build. At that moment, Next.js must do several things before it can serve a stable cached response:
- Resolve the dynamic segment.
- Run the server component tree.
- Execute data fetching.
- Render the HTML and RSC payload.
- Write the generated artifact into the incremental cache.
In local development this may look acceptable. In a containerized production environment, several factors amplify the delay:
- Ephemeral filesystems: ISR cache writes may land on container storage that is slower or not shared.
- Multiple replicas: one pod generates a page, another pod does not yet have that cache entry.
- Cold startup effects: fresh pods may have no warmed runtime state.
- Shared volume latency: if cache persistence uses a mounted volume, file I/O can become a bottleneck.
- Data source latency: the first uncached page often triggers full upstream fetch cost.
The important technical point is this: dynamicParams = true does not mean unknown pages are instantly available from cache. It means they are allowed to be generated at request time. If your deployment makes request-time generation expensive, the first user pays for that cost.
This is why the issue shows up more clearly in Docker on Kubernetes than on a single local node. The rendering behavior is valid, but the architecture makes the fallback generation path feel much slower than expected.
Step-by-Step Solution
The fix is usually not a single line change. The reliable solution is to reduce or eliminate expensive on-demand generation for popular paths and make cache behavior predictable across instances.
1. Keep only truly predictable pages in generateStaticParams()
If you know your hottest slugs in advance, prebuild them. Leave the long tail for runtime generation.
export async function generateStaticParams() {
const popularSlugs = await getPopularSlugs()
return popularSlugs.map((slug) => ({ slug }))
}
export const dynamicParams = true
export const revalidate = 300
This reduces the number of users who ever hit the slow uncached path.
2. Make sure your data fetching is cache-aware
If the page fetches remote data without proper caching semantics, every first render becomes heavier than necessary.
async function getPost(slug) {
const res = await fetch(`https://example.com/api/posts/${slug}`, {
next: { revalidate: 300 }
})
if (!res.ok) {
throw new Error('Failed to fetch post')
}
return res.json()
}
export default async function Page({ params }) {
const post = await getPost(params.slug)
return <article>{post.title}</article>
}
Use next: { revalidate } or other deliberate caching patterns so the upstream fetch layer does not sabotage ISR performance.
3. Avoid treating runtime-generated pages as if they were build-time static pages
If a route has a massive number of possible params, fully leaning on ISR for first-hit generation may be the wrong strategy. In that case, consider dynamic rendering for the long tail.
export const dynamic = 'force-dynamic'
Use this only if the page is better served dynamically than through expensive one-time static generation. It trades cacheability for predictability.
4. Persist cache correctly across Kubernetes replicas
If you run multiple pods, each pod may generate the same uncached path independently unless the cache layer is shared or the traffic is sticky. Review your deployment architecture:
- Use a platform-supported shared ISR/data cache strategy where available.
- Ensure replicas are not discarding generated artifacts immediately.
- Verify whether your volume setup introduces high write latency.
- Consider reducing replica divergence during testing by running one replica first.
This is often the hidden reason teams think ISR is broken when the real issue is cache locality.
5. Pre-warm critical uncached routes after deployment
If you know which slugs are likely to be visited soon after release, trigger them once after deploy so users do not pay the generation cost.
const slugsToWarm = ['post-1', 'post-2', 'post-3']
async function warmPages() {
await Promise.all(
slugsToWarm.map((slug) =>
fetch(`https://your-app.example.com/${slug}`)
)
)
}
warmPages().catch(console.error)
Run this from a job, deployment hook, or release pipeline.
6. Measure whether the delay is rendering, fetch, or storage
Before changing everything, profile the request path. Log timings around data fetches and compare:
- pre-generated slug response time
- first request to uncached slug
- second request to same slug
- same slug across different pods
If the second request is fast only on the same pod, the problem is likely cache sharing. If both first and second requests stay slow, the bottleneck is probably upstream fetches or dynamic work inside the page.
7. Example route setup for controlled ISR
export const revalidate = 300
export const dynamicParams = true
export async function generateStaticParams() {
const featured = await getFeaturedSlugs()
return featured.map((slug) => ({ slug }))
}
async function getPageData(slug) {
const res = await fetch(`https://example.com/api/page/${slug}`, {
next: { revalidate: 300 }
})
if (res.status === 404) {
return null
}
if (!res.ok) {
throw new Error('Upstream fetch failed')
}
return res.json()
}
export default async function Page({ params }) {
const data = await getPageData(params.slug)
if (!data) {
notFound()
}
return <main><h1>{data.title}</h1></main>
}
This setup works well when:
- high-traffic pages are pre-generated
- long-tail pages are allowed at runtime
- fetches are cached intentionally
- your deployment supports cache persistence sensibly
Common Edge Cases
Multiple pods generating the same page simultaneously
Two requests for the same uncached slug can hit different replicas and both pay the generation cost. This creates duplicate work and inconsistent perceived performance.
404 behavior for unknown params
If your upstream data source returns missing content slowly, the first request to a non-existent slug can still be expensive. Handle notFound() early and make upstream 404 checks efficient.
Slow shared storage
Mounting persistent volumes can help retain generated artifacts, but slow network storage can make ISR writes sluggish. Persistence alone does not guarantee speed.
Over-fetching inside server components
If nested components each fetch independently, the first render for uncached content can become much slower than expected. Deduplicate and centralize critical data fetching where possible.
Confusion between route-level ISR and fetch-level caching
A route may look statically optimized while individual fetch calls disable caching or use incompatible settings. Review both layers together.
Load balancer masking cache success
You may test the same path twice and still see a slow response if the second request lands on a different pod with a cold cache. This makes debugging very misleading.
FAQ
Why is only non-generated content slow while generated content is fast?
Because generated content already exists at build time. Non-generated content must be rendered and cached on the first request, so the user experiences the full cost of runtime generation.
Does dynamicParams=true guarantee good ISR performance for unknown slugs?
No. It only allows unknown params to be generated at runtime. Performance depends on fetch latency, rendering cost, cache persistence, and how your Kubernetes deployment distributes traffic.
Should I remove generateStaticParams() to fix this?
Not necessarily. generateStaticParams() is still valuable for high-traffic pages. The better fix is usually to prebuild the hottest routes, optimize fetch caching, and make sure your deployment does not fragment the incremental cache across replicas.
The practical takeaway is simple: when using ISR with generateStaticParams() and dynamicParams = true, treat unknown params as runtime-generated pages, not magically pre-optimized static assets. Once you align caching, pod behavior, and data fetching with that model, the long delay for not-generated content becomes understandable and manageable.