Dynamic route works in dev but fails in production with next build
next build && next start? We have a project we are working on that has a dynamic
pathbeing created in the query to sanity, and we are using a wildcard
[path].tsxto catch those routes. The query seems to work fine, and in dev we can get to all the pages using this route. But in production we get empty pages for
/page1, and 404s for
/page1/childpage
This is a classic Next.js static generation issue! The problem is almost certainly that your dynamic routes aren't being pre-rendered at build time. Here's what's happening:
The Root Cause:
In development mode (next dev), Next.js generates pages on-demand for every request. But in production (next build && next start), Next.js uses static generation by default, which means pages need to be either:
- Pre-rendered at build time, OR
- Configured to generate on-demand with proper fallback handling
Since you're using a catch-all route like [path].tsx, you need to implement getStaticPaths() (Pages Router) or generateStaticParams() (App Router) to tell Next.js which paths to pre-render.
The Solution:
For Pages Router ([path].tsx):
export async function getStaticPaths() {
const paths = await client.fetch(
`*[_type == "yourType" && defined(path)][].path`
);
return {
paths: paths.map((path) => ({ params: { path } })),
fallback: 'blocking', // or true, see below
};
}
export async function getStaticProps({ params }) {
const { path } = params;
const data = await client.fetch(
`*[_type == "yourType" && path == $path][0]`,
{ path }
);
return {
props: { data },
revalidate: 60, // Optional: ISR
};
}For App Router (app/[path]/page.tsx):
export async function generateStaticParams() {
const paths = await client.fetch(
`*[_type == "yourType" && defined(path)][].path`
);
return paths.map((path) => ({ path }));
}Understanding Fallback Options:
The fallback property in getStaticPaths is crucial:
fallback: false- Returns 404 for any path not generated at build timefallback: true- Generates pages on-demand, but requires loading state handlingfallback: 'blocking'- Generates pages on-demand and waits before serving (no loading state needed)
Why You're Seeing Different Behaviors:
/page1showing empty: The page is rendering butgetStaticPropsisn't fetching data because the path wasn't pre-rendered/page1/childpageshowing 404: Next.js doesn't know this route exists at all
For Catch-All Routes Specifically:
If you're using [...path].tsx to capture nested routes like /page1/childpage, make sure your params structure matches:
// For [...path].tsx (catch-all)
paths: allPaths.map((pathString) => ({
params: { path: pathString.split('/') } // Array for catch-all
}))
// For [path].tsx (single segment)
paths: allPaths.map((pathString) => ({
params: { path: pathString } // String for single segment
}))The key takeaway: production builds require explicit path generation. Without getStaticPaths or generateStaticParams, Next.js doesn't know which dynamic routes to create at build time!
Show original thread18 replies
Was this answer helpful?
Sanity – Build the way you think, not the way your CMS thinks
Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.