Dynamic Segments and Route Groups
The idea in one sentence
Section titled “The idea in one sentence”Bracketed folders turn URL parts into params you read from a Promise, while parenthesized and underscored folders reshape your file tree without changing the URL.
Dynamic segments read from a Promise
Section titled “Dynamic segments read from a Promise”A folder named [id] captures one URL part as a param. In current Next.js params is a Promise, so you await it. The same is true for searchParams.
export default async function Post({ params, searchParams,}: { params: Promise<{ id: string }> searchParams: Promise<{ [key: string]: string | string[] | undefined }>}) { const { id } = await params const { sort } = await searchParams return <article>Post {id} sorted by {sort}</article>}Catch-all and optional catch-all
Section titled “Catch-all and optional catch-all”Use [...slug] to capture many segments as an array. Use [[...slug]] to make it optional so the parent route also matches with no extra segments.
app/ docs/ [...slug]/page.tsx // matches /docs/a and /docs/a/b/c shop/ [[...slug]]/page.tsx // matches /shop AND /shop/a/bexport default async function Docs({ params,}: { params: Promise<{ slug: string[] }>}) { const { slug } = await params return <h1>{slug.join(' / ')}</h1>}generateStaticParams prerenders dynamic routes
Section titled “generateStaticParams prerenders dynamic routes”Export generateStaticParams to tell Next.js which param values to prerender at build time. Each returned object becomes one statically generated page.
export function generateStaticParams() { return [{ id: '1' }, { id: '2' }, { id: '3' }]}Route groups and private folders
Section titled “Route groups and private folders”A folder in parentheses like (marketing) is a route group: it organizes files and can hold its own shared layout, but its name is stripped from the URL. A folder with a leading underscore like _components is a private folder that opts out of routing entirely.
app/ (marketing)/ layout.tsx // layout for marketing pages only about/page.tsx // URL is /about, NOT /marketing/about (shop)/ cart/page.tsx // URL is /cart _components/ // private, never a route Button.tsxgraph TD A["[id] dynamic segment"] --> U1["changes URL: /blog/42"] B["[...slug] catch-all"] --> U2["changes URL: /docs/a/b"] C["(marketing) route group"] --> U3["no URL change"] D["_components private"] --> U4["not routable"]