Next.js output export: generateStaticParams Error Fix
Next.js output: export fails with missing generateStaticParams() on dynamic routes. Fix it with a query-param page, tested on Next.js 16.3.5. See the steps.

If next build stops with Page "/campaigns/[id]" is missing "generateStaticParams()" so it cannot be used with "output: export" config, Next.js is telling you that a static export cannot contain a route whose parameter values are unknown at build time. When the IDs are created at runtime, no list you can return from generateStaticParams() will ever be complete.
The fix that works for runtime IDs is to stop putting the ID in the path. Move it to the query string, serve one static page such as /campaigns/detail?id=42, and read the ID in a client component wrapped in Suspense. I verified every step below against a clean Next.js 16.3.5 project.
Quick Fix
Replace the dynamic folder with a fixed one and read the ID on the client.
// app/campaigns/detail/CampaignDetail.tsx
"use client";
import { useSearchParams } from "next/navigation";
export default function CampaignDetail() {
const id = useSearchParams().get("id");
return <h1>Campaign {id ?? "not selected"}</h1>;
}// app/campaigns/detail/page.tsx
import { Suspense } from "react";
import CampaignDetail from "./CampaignDetail";
export default function Page() {
return (
<Suspense fallback={<p>Loading campaign...</p>}>
<CampaignDetail />
</Suspense>
);
}Delete app/campaigns/[id], then link with /campaigns/detail?id=42. That is the whole change for most admin dashboards.
Why Does Next.js Static Export Reject Dynamic Routes?
A static export writes plain HTML files at build time, so every URL must map to a file that already exists. The Next.js static export guide (checked against the v16.2.9 docs) lists Dynamic Routes without generateStaticParams() as unsupported, along with rewrites, redirects, headers, Proxy, Server Actions, and the default image loader.
This is the exact output I got from a fresh project with output: "export" and a single app/campaigns/[id]/page.tsx:
▲ Next.js 16.3.5 (Turbopack)
Collecting page data using 5 workers ...
> Build error occurred
Error: Page "/campaigns/[id]" is missing "generateStaticParams()" so it cannot be used with "output: export" config.Older versions word it slightly differently, for example Page "/[[...slug]]/page" is missing exported function "generateStaticParams()", which is what you will see in many GitHub issues. Same cause.
The second half of the problem shows up after you “solve” the build error by listing a few IDs. A static host such as Cloudflare Pages serves a file that matches the path. Open /campaigns/999 directly, or refresh it, and there is no 999.html, so you get a 404. Cloudflare Community threads about 404s on direct navigation to dynamic Next.js routes describe exactly this.
Fix 1: Query-Param Page (Best for Runtime IDs)
Use this when the IDs come from a database or API and the page sits behind a login, so search engines never need to index each record.
The build output shows what you get. One route, one HTML file, for every ID:
Route (app)
┌ ○ /
├ ○ /_not-found
└ ○ /campaigns/detail
○ (Static) prerendered as static contentout/404.html
out/campaigns/detail.html
out/index.htmlThree details matter.
The Suspense boundary is not optional. Without it, the build fails with this message, which I also captured:
useSearchParams() should be wrapped in a suspense boundary at page "/campaigns/detail".
Read more: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailoutThe Next.js docs explain why: on a prerendered route, useSearchParams forces the client component tree up to the nearest Suspense boundary to render on the client. Only the fallback is baked into the HTML. I checked out/campaigns/detail.html and it contains Loading campaign..., and the real content appears after hydration.
Keep the hook in a small client component, as in the Quick Fix, and leave page.tsx as a server component. That way only the part that needs the query string bails out of prerendering.
Update every place that builds the URL:
import Link from "next/link";
import { useRouter } from "next/navigation";
// list row
<Link href={{ pathname: "/campaigns/detail", query: { id: campaign.id } }}>Open</Link>;
// after creating a record
const router = useRouter();
router.push(`/campaigns/detail?id=${newId}`);An edit page follows the same pattern: /campaigns/edit?id=42. Handle the missing case, because anyone can open /campaigns/detail with no id. The id ?? "not selected" fallback above is the minimum.
Fix 2: Placeholder Param Plus a Rewrite Rule
If you must keep clean URLs like /campaigns/42, you can export one placeholder page and let the host serve it for every ID. I built this variant too:
// app/campaigns/[id]/page.tsx
export async function generateStaticParams() {
return [{ id: "_" }];
}The build passes and writes a single file, out/campaigns/_.html. A _redirects rule that rewrites /campaigns/* to /campaigns/_ with status 200 is a commonly suggested approach in Cloudflare Community answers, and then the client reads the real ID from usePathname(), because params.id is baked in as _. I only ran the build for this variant, not a Cloudflare deploy, so test the rewrite in a preview deployment before trusting it.
The cost is a second source of truth for routing, split between Next.js and a host-specific file. It also breaks the day you move to a host with different rewrite syntax.
Fix 3: Drop Static Export
If you need server rendering, real dynamic segments, or Server Actions, static export is the wrong tool. Note the current state of Cloudflare deployment, because many older tutorials are stale. The @cloudflare/next-on-pages package is deprecated, and its GitHub repo was archived in September 2025. Cloudflare’s own docs now point Next.js apps to Workers with the OpenNext adapter, which runs on the Node.js runtime instead of Edge.
That is a real migration, so only take it when you need a server. For an authenticated dashboard that just lists and edits records through an API, a static export with query-param pages is simpler to host and cheaper to run. If you do self-host a server later, the trade-offs in Docker vs PM2 for Next.js on VPS: The 2026 Deployment Guide apply.
Also Read: Next.js 16 Release: Blazing Fast Startup & Build Stability
Old URLs After the Refactor
Redirects declared in next.config do not run in a static export, since the docs list them as unsupported. If bookmarks like /campaigns/42 must keep working, handle them at the host with a _redirects rule that maps the old path to the new query-param URL, and verify it in a preview deploy first. For an internal dashboard, telling the team to update bookmarks is often the pragmatic answer.
How I Ran Into This
I hit this in an admin dashboard for managing ad campaigns, built as a static export and served from Cloudflare Pages. It had campaigns/[id] for detail and edit pages, and IDs were created by users at runtime. Pre-generating IDs was impossible, and direct visits to a detail URL would 404 on a static host.
I replaced the dynamic route with query-param detail and edit pages. One page now serves every campaign and nothing needs to be listed at build time. The trade-off is uglier URLs, which did not matter for a page behind a login. If you are deciding what else belongs on a server, Server Actions vs Client Rendering in Next.js: The 2025 Guide covers the split.
Which Fix Should You Pick?
| Option | Clean URLs | Runtime IDs | Host config | What I verified |
|---|---|---|---|---|
| Query-param page | No | Yes | None | Build and output files |
| Placeholder plus rewrite | Yes | Yes | _redirects rule | Build only |
| Drop static export | Yes | Yes | Server or Workers | Not tested |
Use the query-param page when IDs are runtime data and the pages are not meant to rank in search. Use the placeholder and rewrite only if clean URLs are a hard requirement and you control the host config. Leave static export when you need a server, and plan the move to Workers with OpenNext instead of reaching for the deprecated Pages adapter.
Also Read: Next.js RCE Patch 2026: Fix It, Then Harden Your CSP

![Astro 5 Hydration Mismatch: Causes and Fixes [2026]](https://cdn.asepalazhari.com/images/articles/development/fixing-astro-hydration-mismatch-errors.jpeg)
