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;

What do you think?

Please let me know what you think about this post on the forum.

You can find the discussion about this post here.

Prabashwara Seneviratne

Written by

Prabashwara Seneviratne

(bash)