Dynamic Segments
Name a folder [param] to capture a part of the URL, then read it from the async params prop.
Syntax
app/blog/[slug]/page.tsxTo build pages for many items (blog posts, products, users) you use a dynamic segment: a folder named with square brackets, like [id]. Whatever value appears in that URL position is passed to your page.
Reading params (Next 15+)
In the App Router, params is a Promise. Make your page async and await it before using the values.
Example
// app/blog/[slug]/page.tsx -> /blog/anything
export default async function Post({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
return <h1>Post: {slug}</h1>;
}When to use it
- A blog reads the [slug] param to fetch and render the correct post from a CMS.
- A product page at /products/[id] fetches product details from an API using the id from the URL.
- A user profile page at /users/[username] displays profile data by passing the username segment to a database query.
More examples
Dynamic segment folder
Brackets around a folder name make it a dynamic segment that captures any value at that position.
app/
└── blog/
└── [slug]/
└── page.tsx # → /blog/my-first-postReading params in a page
The params prop is a Promise in Next.js 15+, so it must be awaited to read the segment value.
// app/blog/[slug]/page.tsx
type Props = { params: Promise<{ slug: string }> };
export default async function PostPage({ params }: Props) {
const { slug } = await params;
return <h1>Post: {slug}</h1>;
}generateStaticParams for SSG
generateStaticParams pre-renders every post at build time instead of rendering on demand.
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await fetch('/api/posts').then(r => r.json());
return posts.map((p: { slug: string }) => ({ slug: p.slug }));
}
export default async function PostPage({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
return <h1>{slug}</h1>;
}
Discussion