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 PostFrom the above code, there are different routes and queries that can work.
Route:
/post/abcHas this query:
{ "pid": "abc" }
Route:
/post/abc?foo=barHas 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=123Has this query: {
"pid": "abc" }
The above also works when you have multiple dynamic route segments.
Page:
pages/post/[pid]/[comment].jsHas this route:
/post/abc/a-commentHas 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].jsmatches 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]].jsand the routes can be/post,/post/aand/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.jsWill match
/post/create
pages/post/[pid].jsWill match
/post/1.Will match
/post/abc.Will not match
/post/create.Will not match
/post/abc.