Technology · Next.js
Next.js Dynamic Routes
Create dynamic pages with brackets, access route params, and generate static paths with generateStaticParams.
TL;DR
- 01Wrap folder names in brackets to create dynamic segments.
- 02Access params through the params prop passed to pages.
- 03Use generateStaticParams to pre-build dynamic pages at compile time.
Basic Dynamic Routes
- Create a dynamic route by wrapping a folder name in square brackets.
app/ posts/ [id]/ page.tsx - This matches
/posts/1,/posts/2, or any value for theidsegment. - Access the dynamic parameter through the
paramsprop in the page component.export default function Post({ params }: { params: { id: string } }) { return <h1>Post {params.id}</h1>; } - Each dynamic segment creates a separate route that can load different data.
- URL parameters are always strings, so parse them as needed.
Accessing Route Parameters
- The params object contains all dynamic segments from the URL path.
// Route: app/users/[userId]/posts/[postId]/page.tsx export default function PostPage({ params }: { params: { userId: string; postId: string } }) { return <div>User {params.userId}, Post {params.postId}</div>; } - Fetch data using the params to load content specific to that route.
const post = await getPost(params.id); - Pass params to layout components if they need this information.
- Nested dynamic segments work just like single-level ones.
Catch-All Routes
- Use
[...slug]to capture all remaining segments in one parameter.app/ docs/ [...slug]/ page.tsx - This matches
/docs/a,/docs/a/b,/docs/a/b/c, etc. - The
slugparam is always an array of path segments.export default function Docs({ params }: { params: { slug: string[] } }) { const path = params.slug.join("/"); return <h1>{path}</h1>; } - Use catch-all routes for nested documentation or content sites.
- Combine with optional catch-all
[[...slug]]for breadcrumb navigation.
Optional Catch-All Routes
- Use
[[...slug]]to make the catch-all optional, matching parent too.app/ blog/ [[...slug]]/ page.tsx - This matches
/blog,/blog/post-1,/blog/2025/january/post, etc. - The slug param is an array only if segments exist, otherwise undefined.
const slug = params.slug ?? []; const depth = slug.length; - Use optional catch-all when a single page handles multiple URL patterns.
- Great for flexible navigation menus and hierarchical content.
Static Generation with Dynamic Routes
- Export
generateStaticParams()to pre-build specific dynamic pages.export async function generateStaticParams() { const posts = await getPosts(); return posts.map((post) => ({ id: post.id.toString(), })); } export default function Post({ params }) { // This page is pre-built for every post at build time return <h1>Post {params.id}</h1>; } - Pre-built pages load instantly with no server delay.
- Pages not in generateStaticParams are generated on-demand the first time.
- Use
revalidatefor incremental static regeneration to update stale pages.export const revalidate = 3600; // Rebuild every hour - Great for blogs, product pages, and public documentation.
Tip: Use
generateStaticParams()for high-traffic pages like blog posts to pre-build them at deploy time for instant page loads.
Warning: If generateStaticParams doesn't include a route, it will be generated on first request which causes a slow cold start on serverless.