Dynamic Routes and Reading Params
11 min read
Creating Dynamic URL Segments
Wrap a folder name in square brackets to create a dynamic segment — a wildcard that matches any value. The matched value is passed as a prop to the page component.
text
src/app/
blog/
[slug]/
page.tsx -> /blog/hello-world, /blog/nextjs-tips, ...In modern Next.js (14+), the params prop is asynchronous and must be awaited before you can read from it. This is a breaking change from older versions — forgetting to await params is a very common source of bugs.
tsx
// src/app/blog/[slug]/page.tsx
type Props = { params: Promise<{ slug: string }> };
export default async function BlogPostPage({ params }: Props) {
const { slug } = await params; // MUST await params
const post = await fetchPostBySlug(slug);
return (
<article>
<h1>{post.title}</h1>
<p>{post.body}</p>
</article>
);
}
async function fetchPostBySlug(slug: string) {
const res = await fetch(`https://api.example.com/posts/${slug}`, {
next: { revalidate: 3600 },
});
if (!res.ok) throw new Error("Post not found");
return res.json();
}Catch-All Segments
Use [...slug] for a catch-all that matches /docs/a, /docs/a/b, /docs/a/b/c and so on. Use [[...slug]] for an optional catch-all that also matches /docs.
Always type params as Promise<{ ... }> and await it. Accessing params.slug synchronously without awaiting will produce a deprecation warning in Next.js 14 and may break in Next.js 15+.
Ready to test yourself?
Practice Next.js— quiz & coding exercises