Dynamic Routes
Although it is very nice that Next.js defines routes by using predefined paths, it is not always enough for more complex applications.
A way to create more complex routes is by adding brackets to the route, this will turn the route into a dynamic route and adds a bunch of functionality.
There are a few ways to make routing a more complex project.
Adding [ ]
to a route
Let's look at the little bit of example code below.
This is a dynamic routing file.
// file: pages/post/[pid].js
import { useRouter } from 'next/router'
const Post = () =>{
const router = useRouter()
const { pid } = router.query
return <p>Post: {pid}</p>
}
export default Post
From the above code, there are different routes and queries that can work.
Route:
/post/abc
Has this query:
{ "pid": "abc" }
Route:
/post/abc?foo=bar
Has this query:
{ "foo": "bar", "pid": "abc" }
Important is to know that route parameters will override query parameters if they have the same name.
Route:
/post/abc?pid=123
Has this query: {
"pid": "abc" }
The above also works when you have multiple dynamic route segments.
Page:
pages/post/[pid]/[comment].js
Has this route:
/post/abc/a-comment
Has this query:
{ "pid": "abc", "comment": "a-comment" }
When working with client-side navigation to dynamic routes, this is handled with next/link
.
Above routes turned in to links
<ul>
<li>
<Link href="/post/abc">
<a>Go to pages/post/[pid].js</a>
</Link>
</li>
<li>
<Link href="/post/abc?foo=bar">
<a>Also goes to pages/post/[pid].js</a>
</Link>
</li>
<li>
<Link href="/post/abc/a-comment">
<a>Go to pages/post/[pid]/[comment].js</a>
</Link>
</li>
</ul>
Catching all routes
When using dynamic routes, you can extend to catch all paths by adding three dots inside the backets:
pages/post/[...slug].js
matches all of the following routes:/post/a
/post/a/b
/post/a/b/c
How does this work?
When you use a matched parameter, it will be sent as a query parameter to the page. But, it will also be an array. So the query object looks different.
For the route
/post/a
, the query object will be{ "slug": ["a"] }
.And for the route
/post/a/b/c
, the query object will be{ "slug": ["a", "b", "c"] }
.
Using optional catch all routes
You can create optional catch all routes by including the parameter inside of double brackets like [[...slug]]
.
The page will be
pages/post/[[..slug]].js
and the routes can be/post
,/post/a
and/post/a/b
.The difference between catch all and optional catch all routes is that with optional catch all routes the route without the parameter is also matched.
Query objects would be like this:
{ } // GET `/post` which is an empty object
{ "slug": ["a"] } // `GET /post/a` (single-element array)
{ "slug": ["a", "b"] } // `GET /post/a/b` (multi-element array)
Caveats
When working with dynamic routing, there are two rules that are very important.
Predefined routes take precedence over dynamic routes.
Dynamic routes take precedence over catch all routes.
The above rules would lead to the following:
pages/post/create.js
Will match
/post/create
pages/post/[pid].js
Will match
/post/1
.Will match
/post/abc
.Will not match
/post/create
.Will not match
/post/abc
.