Dynamic Routes: URLs with Parameters
Static routes are great, but real apps need dynamic URLs. Blog posts, user profiles, product pages. The URL contains a variable part. Next.js handles this with bracket notation [param].
Basic Dynamic Route
src/app/blog/[slug]/page.tsx
tsx
1// src/app/blog/[slug]/page.tsx2// Matches: /blog/hello-world, /blog/nextjs-guide, etc.34export default async function BlogPost({5params,6}: {7params: Promise<{ slug: string }>;8}) {9const { slug } = await params;1011return (12 <article>13 <h1>Post: {slug}</h1>14 </article>15);16}⚠️
Next.js 15 change
In Next.js 15, params is now a Promise. You need to await it. This was a breaking change from v14 where params was a regular object. Don't forget the async/await!
Catch-All Routes
Need to catch multiple path segments? Use [...param] for catch-all or [[...param]] for optional catch-all:
src/app/docs/[...slug]/page.tsx
tsx
1// src/app/docs/[...slug]/page.tsx2// Matches: /docs/intro, /docs/api/auth, /docs/api/auth/oauth34export default async function DocsPage({5params,6}: {7params: Promise<{ slug: string[] }>;8}) {9const { slug } = await params;10// slug = ["api", "auth", "oauth"] for /docs/api/auth/oauth1112return (13 <div>14 <p>Path: {slug.join(" > ")}</p>15 </div>16);17}src/app/shop/[[...categories]]/page.tsx
tsx
1// src/app/shop/[[...categories]]/page.tsx2// Matches: /shop, /shop/electronics, /shop/electronics/phones3// The [[...]] makes it OPTIONAL, so /shop alone also works45export default async function ShopPage({6params,7}: {8params: Promise<{ categories?: string[] }>;9}) {10const { categories } = await params;1112if (!categories) {13 return <h1>All Products</h1>;14}1516return <h1>Category: {categories.join(" > ")}</h1>;17}Route Groups (Parentheses)
Remember from the layouts chapter, parentheses () create groups that don't affect the URL:
Route groups for different layouts
text
1src/app/2├── (auth)/3│ ├── layout.tsx ← Auth layout (centered card)4│ ├── login/page.tsx → /login5│ └── register/page.tsx → /register6├── (app)/7│ ├── layout.tsx ← App layout (sidebar + header)8│ ├── dashboard/page.tsx → /dashboard9│ └── settings/page.tsx → /settings🚀
Pattern I use often
I group my routes by layout:
• (marketing) for landing pages with a simple header
• (app) for the authenticated dashboard with a sidebar
• (auth) for login/register with a centered card layout
Clean separation, clean URLs.
• (marketing) for landing pages with a simple header
• (app) for the authenticated dashboard with a sidebar
• (auth) for login/register with a centered card layout
Clean separation, clean URLs.