Josué Hernández
Dynamic routing is one of the core features of Next.js, enabling developers to create flexible, scalable, and modern web applications. With Next.js 15, improvements in TypeScript integration and enhanced route handling make it easier than ever to work with dynamic routes. This guide will cover everything from basic dynamic routes to advanced techniques like optional parameters and TypeScript usage.
In Next.js, you can create dynamic routes by using square brackets ([]) in file or folder names within the app directory.
File structure:
app/blog/[slug]/page.tsx
Accessing the route:
URL: /blog/my-first-post
Params: { slug: 'my-first-post' }
This captures the dynamic segment slug and makes it available as a parameter.
Catch-all routes capture multiple dynamic segments in a single parameter using [...param].
File structure:
app/shop/[...slug]/page.tsx
Examples:
Optional catch-all parameters are created using [[...param]], allowing the route to match with or without parameters.
File structure:
app/shop/[[...slug]]/page.tsx
Examples:
Optional parameters make it easier to handle routes like /shop and /shop/a within the same file.
TypeScript allows you to define and enforce parameter types for better type safety and maintainability.
Single Dynamic Parameter:
// app/blog/[slug]/page.tsx
interface BlogParams {
slug: string;
}
Catch-All Parameters:
// app/shop/[...slug]/page.tsx
interface ShopParams {
slug: string[];
}
Optional Catch-All Parameters:
// app/shop/[[...slug]]/page.tsx
interface OptionalShopParams {
slug?: string[];
}
When a route contains multiple dynamic segments, define an interface for all parameters:
File structure:
app/[categoryId]/[itemId]/page.tsx
TypeScript:
interface Params {
categoryId: string;
itemId: string;
}
Next.js 15 handles query strings (search parameters) seamlessly in page.tsx.
URL:
/shop?a=1&b=2
Captured searchParams:
{ a: '1', b: '2' }
Duplicate query parameters:
/shop?a=1&a=2
Captured searchParams:
{ a: ['1', '2'] }
You can access searchParams in page.tsx to customize page rendering based on the query string.
Here’s a complete example that demonstrates dynamic routing with parameters and search queries:
interface CategoryProps {
params: { categories: string[] };
searchParams: { id: string };
}
export default function Category({ params, searchParams }: CategoryProps) {
const { categories } = params;
const { id } = searchParams;
return (
<>
<h2>Category: {categories.join(', ')}</h2>
<p>ID: {id}</p>
</>
);
}
For /category/electronics/computers?id=456:
Category: electronics, computers
ID: 456
This demonstrates how to handle both dynamic segments (params) and query strings (searchParams) in a clean and efficient way.
Next.js 15 introduces a significant change in how route parameters (params and searchParams) are handled. Unlike previous versions, where parameters were directly passed as plain objects, in Next.js 15, they are now wrapped in a Promise and must be resolved using await before use.
In Next.js 15, route parameters must be awaited before use:
interface ProductProps {
params: Promise<{ productId: string }>;
}
export default async function ProductPage({ params }: ProductProps) {
const { productId } = await params;
const product = await fetchProductFromDB(productId);
return <h1>Product: {product.name}</h1>;
}
This allows dynamic pages to seamlessly integrate with asynchronous data sources, improving both performance and developer experience.
This enhancement in Next.js 15 makes dynamic routes even more powerful, flexible, and optimized for real-world applications.
Dynamic routing in Next.js 15 provides powerful tools to create flexible and scalable applications. This blog introduced the basics of dynamic and optional routes, TypeScript usage, and handling search parameters.
This is just the beginning—future posts will dive deeper into advanced routing strategies and complex examples to help you unlock the full potential of Next.js dynamic routes.