Next.js has file-system based routing built-in. You don't have to explicitly define the path in the router. It's as easy as adding a file inside the folder and you are done. There are multiple ways you can define a path using the file structure. Anything we add inside the pages
directory is served as a path. Definitely there are exceptions like _app.js
and _document.js
.
There are two types of routes in Next.js
- API Routes: Any file you put inside
pages/api
is treated as an API endpoint and is served athttps://hostname/api/*
. - Page Routes: Any file which is inside
pages
can be served as a web page to the user, it can be a Static Generation or Server-side Rendered. Any file which is insidepages/api
is not treated as a web page.
We will go through all of these in detail in this blog post.
Setup a Next.js Project
We will first create a Next.js project for this. We will name this nextjs-routing
npx create-next-app
# or
yarn create next-app
Page Routing
- Index Routing
Once your project is ready it should look something like this. You can see that there is index.js
inside the pages folder. So if you run your application and visit http://localhost:3000
you will see the welcome next.js page, which is mapped to the index.js
.
$ tree -I node_modules
.
├── README.md
├── package.json
├── pages
│ ├── _app.js
│ ├── api
│ │ └── hello.js
│ └── index.js
├── public
│ ├── favicon.ico
│ └── vercel.svg
├── styles
│ ├── Home.module.css
│ └── globals.css
└── yarn.lock
Here is how these pages are mapped to path by router.
pages/index.js
-> http://[hostname]/
pages/posts/index.js
-> http://[hostname]/posts/
Any index.js
is mapped to the directory path it belongs to.
- Nested Routing
Now suppose you want to display deeply nested pages like dashboard settings etc.
pages/about.js
-> http://[hostname]/about/
pages/blog/hello.js
-> http://[hostname]/blog/hello
pages/blog/series/hello.js
-> http://[hostname]/blog/series/hello
- Dynamic Routing
Suppose you don't have the exact route beforehand and it depends upon API data or some dynamic data. Here is how you can create these routes and map them.
pages/blog/[slug].js
-> http://[hostname]/blog/:slug
over here slug can be anything and you can use getStaticPaths
to set the dynamic path.
pages/blog/[...slug].js
-> http://[hostname]/blog/hello
as well as http://[hostname]/blog/series/hello
The above example is to catch all route. Basically, it can map a path to any level of nesting which happens after blog/
. If you replace [...slug].js
-> [[...slug]].js
, it will match to /blog
along with other routes like blog/hello
, blog/series/hello
.
You can't use catch-all routes with the directory. Eg:
pages/blog/[[..slug]]/comments.js
will give you Error: Catch-all must be the last part of the URL.
pages/[username]/dashboard
-> http://[hostname]/:username/dashboard
In the above example, a username can be a dynamic value. It can be used to generate user-specific static pages.
API Routing
Next.js
provides a way to directly create API. You can consume these API from anywhere inside your project. Creating an API is pretty much straight forward, you just have to create a file inside pages/api
and it gets mapped to api/*
. It won't get exposed on the client-side and will only be used on the server-side. API routing is very much similar to page routing. Let's see how this works.
- Index Routing
The router will automatically route files named index to the root of the directory.
pages/api/blog/index.js
-> http://[hostname]/api/blog
- Nested Routing
Now suppose you want to create a deeply nested API. You can create nested folder structure files and it will get mapped in the same way.
pages/api/users/blog/create.js
-> http://[hostname]/api/users/blog/create
- Dynamic Routing
You can use brackets to define the dynamic routes for your API.
pages/api/blog/[slug].js
-> http://[hostname]/api/blog/hello
In the above example, the slug can take any value. You can get this value inside your code.
pages/api/blog/[...slug].js
-> http://[hostname]/api/blog/hello
or http://[hostname]/api/blog/hello/world
The above example is to catch all routes. All the routes with api/blog/*
get routed to this.
pages/api/blog/[[...slug]].js
-> http://[hostname]/api/blog
or http://[hostname]/api/blog/hello
or http://[hostname]/api/blog/hello/world
You can't use catch-all routes with the directory. Eg:
pages/api/blog/[[..slug]]/comments.js
will give you Error: Catch-all must be the last part of the URL.
The above example is for optional catch-all routes. Basically it makes the slug optional.
Predefined routes take precedence over dynamic routes. So if you have
pages/api/blog/create.js
andpages/api/blog/[slug].js
thenpages/api/blog/create.js
gets mapped tohttp://[hostname]/api/blog/create
andpages/api/blog/[slug].js
gets mapped tohttp://[hostname]/api/blog/abc
There is no way to specify which REST calls are allowed on an API route, you have to handle them from your handler code.
Now suppose you want to create api/blogs/:slug/comments/:commentId
. People you are familiar with the concept of resources in REST know there are use cases where we might need a nested resource.
pages/api/blogs/[slug]/comments/[commentId].js
-> http://[hostname]/api/blogs/:slug/comments/:commentId
Conclusion
Using Next.js routing is pretty much easy and straightforward and has very less overhead. It covers most of the use cases, I really couldn't think or find a use case which you won't be able to solve it.
If you liked this post please share it with others so that it can help them as well. You can tag me on Twitter @imumesh18. You can subscribe to my newsletter to read more from me.