Next.js Routing Explained: Dynamic Routes, Route Groups & Layouts (Next.js 16)
Master Next.js App Router routing: dynamic segments, catch-all routes, route groups, layouts, loading and error states, parallel routes and navigation, with Next.js 16 code examples.
On this page (11)
Next.js uses file-based routing: folders inside the app directory become URL segments, and a page file makes a route publicly accessible. Dynamic routes use square brackets like [slug], route groups use parentheses like (marketing), and special files such as layout, loading and error add shared UI and states.
This guide covers routing in Next.js 16. If you're brand new, read Getting Started with Next.js first. Knowing these conventions also makes you far better at reviewing what your AI agent builds, which is a core vibe coding skill.
File-based routing basics
| File | URL |
|---|---|
app/page.tsx | / |
app/about/page.tsx | /about |
app/blog/page.tsx | /blog |
app/blog/[slug]/page.tsx | /blog/any-post |
A folder without a page file isn't a route, so you can keep components and utilities next to the pages that use them.
Special files
| File | Purpose |
|---|---|
layout.tsx | Shared UI that wraps child routes and keeps state between navigations |
page.tsx | The unique UI for a route |
loading.tsx | Instant loading state while the page streams in |
error.tsx | Error boundary for the segment (must be a Client Component) |
not-found.tsx | UI shown when you call notFound() |
route.ts | An API endpoint instead of a page (see Route Handlers) |
Dynamic routes
Wrap a folder name in square brackets to capture part of the URL as a parameter.
// app/blog/[slug]/page.tsx
export default async function BlogPost({ params }: PageProps<'/blog/[slug]'>) {
const { slug } = await params;
return <h1>Post: {slug}</h1>;
}
Two things to notice in Next.js 16:
paramsis a Promise. You mustawaitit. Synchronous access was fully removed in Next.js 16, and AI models trained on older code often get this wrong.PagePropsis a global type helper generated by Next.js (runnpx next typegenif your editor doesn't see it), giving you type-safe params.
Prerender dynamic routes with generateStaticParams
For content like blog posts, prerender every page at build time:
export async function generateStaticParams() {
const posts = await getAllPosts();
return posts.map((post) => ({ slug: post.slug }));
}
// Return a 404 for any slug not returned above
export const dynamicParams = false;
This is exactly how this site's blog works, and it's great for SEO because every page is static HTML.
Multiple dynamic segments
app/shop/[category]/[product]/page.tsx → /shop/shoes/runner-2
export default async function Product({ params }: PageProps<'/shop/[category]/[product]'>) {
const { category, product } = await params;
return <h1>{category} / {product}</h1>;
}
Catch-all and optional catch-all routes
app/docs/[...slug]/page.tsxmatches/docs/a,/docs/a/b/c(but not/docs).slugis an array.app/docs/[[...slug]]/page.tsxalso matches/docs, whereslugisundefined.
export default async function Docs({ params }: PageProps<'/docs/[[...slug]]'>) {
const { slug } = await params;
const path = slug ? slug.join('/') : 'index';
return <h1>Docs: {path}</h1>;
}
Route groups
Wrap a folder in parentheses to organize routes without changing the URL:
app/
(marketing)/
layout.tsx ← marketing header/footer
about/page.tsx → /about
pricing/page.tsx → /pricing
(app)/
layout.tsx ← sidebar + auth check
dashboard/page.tsx → /dashboard
Route groups are perfect for giving marketing pages and logged-in app pages completely different layouts.
Layouts
A layout wraps all routes below it and doesn't re-render on navigation:
// app/dashboard/layout.tsx
export default function DashboardLayout({ children }: LayoutProps<'/dashboard'>) {
return (
<div className="flex">
<nav className="w-60">{/* sidebar links */}</nav>
<main className="flex-1">{children}</main>
</div>
);
}
Loading and error states
// app/dashboard/loading.tsx
export default function Loading() {
return <p>Loading dashboard…</p>;
}
// app/dashboard/error.tsx
'use client';
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
return (
<div>
<p>Something went wrong: {error.message}</p>
<button onClick={() => reset()}>Try again</button>
</div>
);
}
Next.js 16.3 also added catchError from next/error for custom error boundaries that can retry Server Components.
Parallel routes
Parallel routes render multiple pages in the same layout using named "slots" (folders starting with @), which is useful for dashboards and modals:
app/dashboard/
@analytics/page.tsx
@analytics/default.tsx
@team/page.tsx
@team/default.tsx
layout.tsx
Since Next.js 16, every slot needs a default.tsx file or the build fails. A default.tsx that returns null is enough.
Navigation
Use the Link component for client-side navigation with automatic prefetching:
import Link from 'next/link';
export default function Nav() {
return (
<nav>
<Link href="/">Home</Link>
<Link href="/blog">Blog</Link>
</nav>
);
}
For navigation in code (after a form submit, for example), use useRouter from next/navigation in a Client Component:
'use client';
import { useRouter } from 'next/navigation';
export default function GoToDashboard() {
const router = useRouter();
return <button onClick={() => router.push('/dashboard')}>Dashboard</button>;
}
In Server Components and Server Actions, use redirect() from next/navigation instead.
Routing best practices
- Keep URLs short, lowercase and descriptive (good for users and SEO).
- Use route groups to separate layouts rather than nesting URLs.
- Add
loading.tsxto slow routes so navigation feels instant. - Use
generateStaticParamsfor content pages. - Call
notFound()for missing records so search engines get a real 404.
Prompting your AI agent about routing
Add a /dashboard area using a (app) route group with its own layout and sidebar.
Pages: /dashboard, /dashboard/settings, /dashboard/projects/[id].
Await params (Next.js 16), add loading.tsx and error.tsx for the group,
and call notFound() when a project id doesn't exist.
More prompt patterns are in our vibe coding prompting guide.
Next steps
- Add backend logic with API Route Handlers
- Protect routes with Next.js proxy (middleware)
- Improve SEO with the Metadata API
- Read the official Next.js routing docs (opens in a new tab)
- #Next.js
- #Routing
- #Tutorial
- #React