Dynamic Segments with Square Brackets
Next.js's App Router creates dynamic route segments by wrapping a folder name in square brackets, such as app/blog/[slug]/page.tsx, which matches any URL like /blog/hello-world or /blog/nextjs-tips. The bracketed name becomes the key in a params object that Next.js passes to the page component, so a request to /blog/hello-world resolves params.slug to the string 'hello-world'. This convention lets a single page component serve an unbounded number of URLs, which is essential for content-driven sites like blogs, e-commerce product pages, or user profiles where the total number of pages isn't known at build time.
Cricket analogy: A dynamic segment like [playerId] is like a scoreboard template that plugs in any player's name and stats, the same physical board serving every player without a custom board built for each one.
Catch-all and Optional Catch-all Routes
Beyond single dynamic segments, Next.js supports catch-all routes using [...slug], which matches any number of path segments after a given point and delivers them as an array, so /docs/a/b/c would produce params.slug = ['a', 'b', 'c']. An optional catch-all route uses double brackets, [[...slug]], which additionally matches the base path itself with no extra segments at all, making params.slug undefined for /docs while still matching /docs/a/b. This pattern is common for documentation sites or CMS-driven pages where content lives in a nested tree of arbitrary depth.
Cricket analogy: A catch-all route is like a wicketkeeper positioned to collect any number of byes down the leg side, regardless of how many runs are attempted, one fielder covering a whole range of outcomes.
Accessing Params in Server and Client Components
In a Server Component page, params is available as a prop passed directly to the default export; as of Next.js 15, params is a Promise that must be awaited, a change from Next.js 14 and earlier where it was a plain object, so upgrading requires updating page signatures to async functions. In Client Components, you can't receive params as a prop the same way, so you instead call the useParams hook from next/navigation, which reads the current route's dynamic segments and returns them synchronously as an object, working identically for both single and catch-all segments.
Cricket analogy: Awaiting params in Next.js 15 is like waiting for the third umpire's review to come back before the scoreboard updates, whereas the old synchronous style was like the on-field umpire's instant, no-review call.
// app/blog/[slug]/page.tsx (Next.js 15 Server Component)
type Props = { params: Promise<{ slug: string }> };
export default async function BlogPost({ params }: Props) {
const { slug } = await params;
const post = await getPostBySlug(slug);
return <article><h1>{post.title}</h1><p>{post.body}</p></article>;
}
// app/blog/[slug]/ShareButton.tsx (Client Component)
'use client';
import { useParams } from 'next/navigation';
export function ShareButton() {
const params = useParams<{ slug: string }>();
return <button onClick={() => navigator.share({ url: `/blog/${params.slug}` })}>Share</button>;
}generateStaticParams for Static Generation
For dynamic routes you want pre-rendered at build time instead of on every request, you export a generateStaticParams function from the page file that returns an array of param objects, one per page Next.js should statically generate, such as [{ slug: 'hello-world' }, { slug: 'nextjs-tips' }]. Any path not included in that array falls back to dynamic rendering at request time by default, unless dynamicParams is explicitly set to false, in which case unmatched paths return a 404. This is the App Router's equivalent of getStaticPaths from the pages router, and combining it with fetch's built-in caching lets you build a fully static blog that still deploys new posts without a full redeploy when dynamicParams remains true.
Cricket analogy: generateStaticParams is like printing scorecards in advance for every confirmed match in a tournament schedule, so fans get them instantly, while a late-added exhibition match gets a hand-written card printed on the spot.
generateStaticParams can be combined with a parent layout's params: pages nested several dynamic segments deep (e.g. app/[category]/[product]/page.tsx) can call the parent's params inside their own generateStaticParams to generate only valid combinations, avoiding a full cartesian product of every category with every product.
In Next.js 15, params (and searchParams) became Promises in both page and layout Server Components as part of enabling partial prerendering. If you upgrade from Next.js 14, code that destructures params synchronously will throw a type error or return a Promise object instead of the expected values — you must await it or use the React use() hook.
- Square-bracket folders like [slug] create dynamic route segments matched into a params object.
- [...slug] creates a catch-all route capturing an array of segments; [[...slug]] makes it optional, also matching the base path.
- In Next.js 15, params is a Promise in Server Components and must be awaited inside an async page function.
- useParams from next/navigation reads dynamic segments synchronously inside Client Components.
- generateStaticParams pre-renders specific dynamic paths at build time, similar to getStaticPaths in the pages router.
- Paths not returned by generateStaticParams render dynamically at request time unless dynamicParams is set to false.
- Nested dynamic segments can use parent params inside generateStaticParams to avoid generating invalid combinations.
Practice what you learned
1. What does the folder name app/products/[id]/page.tsx create?
2. What is the difference between [...slug] and [[...slug]]?
3. In a Next.js 15 Server Component page, how should params typically be accessed?
4. What happens to a dynamic route not covered by generateStaticParams if dynamicParams is left at its default value?
5. Which hook reads dynamic route segments inside a Client Component?
Was this page helpful?
You May Also Like
The Link Component and Navigation
Learn how Next.js's Link component powers fast, client-side navigation with automatic prefetching, and how to complement it with the useRouter and usePathname hooks.
Loading and Error States
Learn how loading.js, error.js, and not-found.js use React Suspense and Error Boundaries to give every route automatic, granular loading and error UI.
Metadata and SEO in Next.js
Learn how to manage titles, descriptions, Open Graph tags, and structured metadata using the App Router's metadata object and generateMetadata, plus sitemap and robots conventions.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics