While building Next.js apps, we often need to create pages that render different content based on the URL parameters passed to them. In this post, I will walk you through how you can work with URL parameters in Next.js.

(Note: The approaches below work only with Next.js's App router. The same functionality can be achieved using the older Pages router but it requires a different approach.)

Handwritten query string setting params to cool

Using dynamic segments

Next.js supports dynamic routes where multiple paths are handled by a single page component. These routes contain "dynamic segments" which are portions of the URL that can vary.

To create a dynamic route, you need to place the page component in a folder named with the name of the segment surrounded by square brackets. For example, to create a product detail page that renders whenever a subpath under /product/ is requested, you can place the page component in a folder named [id] inside a folder named product within the app folder. In this setup, the folder named [id] creates a new dynamic segment named id and Next.js will render the page within it whenever a subpath under /product is requested (e.g: /product/super-product or /product/mega-product). The part of the URL where the dynamic segment is will be parsed by Next.js and provided through the params prop.

app/product/[id]/page.js

const ProductDetailPage = async ({ params }) => {
const { id } = await params;
return (
<div>
I am {id}
</div>
);
}

export default ProductDetailPage;

You can have multiple dynamic segments,

app/product/[id]/[color]/page.js

const ProductDetailPage = async ({ params }) => {
const { id, color } = await params;
return (
<div>
I am {id} in {color}
</div>
);
}

export default ProductDetailPage;

And use catch-all segments to handle deeply nested subpaths. Catch-all segments are created by adding an ellipsis to the name of the dynamic segment folder (e.g. [...id]). In the setup below, the page component will be rendered regardless of how deep the subpath is (e.g. /product/super-product/red/big/ or /product/smol-product). Catch-all segments should be used sparingly since they can lead to page components that do way too much.

app/product/[...description]

const ProductDetailPage = async ({ params }) => {
const { description } = await params;
return (
<div>
I am {description[0]}
{description?.[1] && ` in ${description?.[1]}`}
{description?.[2] && ` and size ${description?.[2]}`}
</div>
);
}

export default ProductDetailPage;

Using query strings

Dynamic routes are a good fit for mandatory parameters that a page can't render without. If you want to pass optional parameters to a page, query strings, also known as search parameters, are a better option.

A query string is the portion of the URL after the ? character. Parameters within a query string are written in the form of key=value and multiple parameters can be placed in a URL by separating them with &.

For example, if you request /products?color=red&size=large from a Next.js server, Next.js will render the page that is set to render when /products is requested, usually app/products/page.js. You can access the query parameters within the page component like this.

app/products/page.js

const ProductsPage = async ({ searchParams }) => {
const { color, size } = await searchParams
if (color && size) {
return (
<div>
Showing all products that are {color} and {size}.
</div>
);
}
if (color || size) {
return (
<div>
Showing all products that are {(color || size)}.
</div>
);
}
return (
<div>
Showing all products.
</div>
);
}

export default ProductsPage;

If you want to pass several values for a single parameter, you can repeat the parameter within the query string. For example, if you request /products?color=red&color=blue, Next.js will turn color into an array.

app/products/page.js

const ProductsPage = async ({ searchParams }) => {
const { color } = await searchParams
if (Array.isArray(color))
return (
<div>
Showing all products that are {color.join(' and ')}
</div>
);
if (typeof color === 'string') {
<div>
Showing all products that are {color}
</div>
}
return (
<div>
Showing all products.
</div>
);
}

export default ProductsPage;
Prabashwara Seneviratne (bash)

Written by

Prabashwara Seneviratne (bash)

Lead frontend developer