Routing, Dynamic Segments & Navigation APIs
Routing in the Next.js App Router is determined strictly by the folder hierarchy inside app/. By nesting folders, adding bracketed dynamic tokens ([id], [...slug]), and applying organizational parentheses ((marketing)), you can construct sophisticated URL schemes without writing custom route configuration tables.
In this lesson, you will master static and nested routing, dynamic parameter extraction, catch-all segments, route groups, and client-side navigation APIs (Link, useRouter, usePathname, useSearchParams).
┌─────────────────────────────────────────────────────────────────────────────┐
│ Next.js App Router Routing Patterns │
├─────────────────────────┬───────────────────────────┬───────────────────────┤
│ Pattern │ Directory Path │ Matching URL Example │
├─────────────────────────┼───────────────────────────┼───────────────────────┤
│ Static Route │ app/about/page.tsx │ /about │
│ Nested Route │ app/blog/news/page.tsx │ /blog/news │
│ Dynamic Segment │ app/posts/[id]/page.tsx │ /posts/123 │
│ Catch-All Segment │ app/docs/[...slug]/page.ts│ /docs/api/v1/users │
│ Optional Catch-All │ app/shop/[[...slug]]/page │ /shop OR /shop/shoes │
│ Route Group (Omit URL) │ app/(auth)/login/page.tsx │ /login (No /auth URL!)│
└─────────────────────────┴───────────────────────────┴───────────────────────┘
1. Dynamic Route Segments ([id] & [slug])
When a route segment is wrapped in brackets, the segment value is passed as a promise to params in Next.js 15+:
// app/blog/[slug]/page.tsx
interface BlogPostPageProps {
params: Promise<{ slug: string }>;
}
export default async function BlogPostPage({ params }: BlogPostPageProps) {
const { slug } = await params;
return (
<article className="max-w-2xl mx-auto py-8">
<h1 className="text-3xl font-bold">Article: {slug}</h1>
<p className="text-slate-600 mt-4">Loaded dynamic article content for {slug}.</p>
</article>
);
}
2. Catch-All & Optional Catch-All Routes
- Catch-All (
[...slug]): Matches one or more segments (e.g./docs/guides/installationproducesslug = ['guides', 'installation']). - Optional Catch-All (
[[...slug]]): Matches zero or more segments (matches/docsas well as/docs/a/b).
// app/docs/[...slug]/page.tsx
interface DocsPageProps {
params: Promise<{ slug: string[] }>;
}
export default async function DocsPage({ params }: DocsPageProps) {
const { slug } = await params;
const path = slug.join(' / ');
return <div>Documentation Breadcrumb: {path}</div>;
}
3. Route Groups ((groupName))
Route groups organize routes without affecting the public URL path. They allow creating distinct layouts for different sections of the app (e.g. (marketing) vs (dashboard)):
app/
├── (marketing)/
│ ├── layout.tsx # Marketing Header & Footer
│ ├── page.tsx # /
│ └── about/page.tsx # /about
└── (app)/
├── layout.tsx # Dashboard Sidebar Shell
└── dashboard/page.tsx # /dashboard
4. Client-Side Navigation APIs
A. <Link> Component (Pre-fetching & Fast Transitions)
<Link> performs client-side transitions and automatically pre-fetches viewport links in production:
import Link from 'next/link';
export function Navigation() {
return (
<nav className="flex gap-4">
<Link href="/dashboard" className="text-blue-600 hover:underline">
Dashboard
</Link>
<Link href="/posts/42" prefetch={true} className="text-blue-600 hover:underline">
Post #42
</Link>
</nav>
);
}
B. Client Navigation Hooks (useRouter, usePathname, useSearchParams)
'use client';
import { useRouter, usePathname, useSearchParams } from 'next/navigation';
export function SearchFilter() {
const router = useRouter();
const pathname = usePathname(); // e.g. "/products"
const searchParams = useSearchParams(); // e.g. "?category=electronics"
const currentCategory = searchParams.get('category');
function handleFilter(category: string) {
const params = new URLSearchParams(searchParams.toString());
params.set('category', category);
router.push(`${pathname}?${params.toString()}`);
}
return (
<div className="flex gap-2">
<button
onClick={() => handleFilter('tech')}
className={`px-3 py-1 rounded ${currentCategory === 'tech' ? 'bg-black text-white' : 'bg-slate-200'}`}
>
Technology
</button>
</div>
);
}
Summary & Key Takeaways
- Folders with bracketed names (
[id]) create dynamic routes whose values are extracted viaparams. - Catch-all routes (
[...slug]) map multi-level paths into string arrays. - Route groups
(group)organize directories and allow distinct layouts without polluting public URL paths. - Always use
next/linkfor internal navigations to benefit from automated prefetching and client-side transitions.
Best Practices & Senior Guidance
- Await
paramsandsearchParamsin Next.js 15+: In modern Next.js,paramsandsearchParamsare asynchronous promises; alwaysawaitthem before reading properties. - Use URL Search Params for Shareable Filters: Storing search filters and pagination in URL query params enables users to bookmark and share filtered views.