
In this tutorial, you'll learn the basics of how to route pages in your Next.js application. Throughout the course of this tutorial, I'll try to explain the different types of routing available in Next.js and how to use them with the help of an example. So, let's get started by creating your Next.js app.
Creating the Next.js App
To get started with creating your Next.js app you'll be requiring Node.js >= 10.13. Type in the following command to check the Node version installed,
node -v
The above command should output something as shown:
C:\Users\Jay>node -v
v18.12.1
If your Node version is above 10.13 you can use the following command to create your Next.js app.
npx create-next-app next-route-app
The above command would ask a couple of questions as shown. For the following, you can select the respective answers,
- Would you like to use TypeScript with this project? Yes
- Would you like to use ESLint with this project? Yes
- Would you like to use
src/
directory with this project? Yes - Would you like to use the experimental
app/
directory with this project? No - What import alias would you like configured? Press Enter
Once this is done you will have your Next.js app created. Navigate to the project directory and start the app.
cd next-route-app
npm run dev
The above command will start the project in development mode and you will have the app running at http://localhost:3000.
Understanding Routing in Next.js
Index routes
If you take a look at the project structure, you'll see a pages
folder inside the src
folder. Next.js router will route index files to the root directory.
For example, inside the pages
folder, we have an index.ts
file. Requests coming to http://localhost:3000/ will be routed to pages/index.ts
.
Let's take another example and create a folder called pages/brands
and inside brands
create a file called index.ts
. Add the following code to the brands/index.ts
file:
export default function Brands() {
return (
<>
<h3>
Welcome to Brands !!
</h3>
</>
)
}
Point your browser to http://localhost:3000/brands and it will route to the index.ts
file inside brands
folder.
So, whenever you redirect to any index routes such as http://localhost:3000/ or http://localhost:3000/brands etc. Next.js will look for index.ts
inside the specific folder.
Static Routes
Now if you have to render some static content then it makes sense to create static routes. For static routes, you can create a file inside the pages folder with the name of the route.
For example, to have a route for displaying the contact us page, I'll create a file called contact-us.tsx
inside the pages
folder. This page can be accessed by using the route http://localhost:3000/contact-us.
The same thing can be done by creating a folder called contact-us
and an index.tsx
file inside it. But since it's a static route it doesn't make sense to do it that way.
For example, we might have other routes like http://localhost:3000/brands/1, http://localhost:3000/brands/2 but there will be only one http://localhost:3000/contact-us hence it makes sense to use static route for such cases.
Dynamic Routing
Taking the above example of brands, if we have a couple of brands we can create 1.tsx
, 2.tsx
, 3.tsx
inside the brands
folder. But it gets complex if we have, let's say, 100 brands. Since in that case, a file needs to be created for each brand which is not practical. That is where dynamic routing comes into the picture.
Instead of creating a file for each brand, we can create a file called [brandId].tsx
inside the brand folder. Here is how it looks:
import { useRouter } from 'next/router'
const Brand = () => {
const router = useRouter()
const { brandId } = router.query
return <h3>Brand: {brandId}</h3>
}
export default Brand
Any brand route requests like http://localhost:3000/brands/1, http://localhost:3000/brands/2 will get matched to brands/[brandId]
.
The brandId
parameter passed will be available in the page via the router.query
.
Save the above changes and check brand route requests. You will be able to see the passed-in brand id being rendered in the page.
Catch all Route
Now there might be scenarios where the route will have a couple of more parameters like http://localhost:3000/brands/in/2 or http://localhost:3000/brands/in/delhi/2 each of which means a different set of parameters. To handle such routes we can define catch-all route by changing the name of the file from [brandId].tsx
to [...params].tsx
.
Now parameters in routes like http://localhost:3000/brands/in/delhi/2 will get interpreted as an array.
params = ['in','delhi','2']
Replace the existing code in [...params.tsx]
as shown:
import { useRouter } from 'next/router'
const Brand = () => {
const router = useRouter()
const { params } = router.query
return (
<>
{
params && params.length &&
<span>
Country: {params[0]} <br />
State: {params[1]} <br />
Brand Id: {params[2]}
</span>
}
</>
)
}
export default Brand
In the above code, once you have received the parameters from router.query
in params
you can use it accordingly. Here we are rendering it to the page.
Save the changes and point your browser to http://localhost:3000/brands/india/delhi/1 and the parameters from the route will get rendered to the page.
Shallow Routing
Next let's have a look at shallow routing. Simply put, shallow routing means changing the page URL without reloading the entire component.
Let's try to understand shallow routing with the help of an example. Create a new page called employees
. For that add a new folder called employees
under src/pages
folder. Inside the src/pages/employees
folder create an index file called index.tsx
. Here is how it looks:
export default function Employees() {
useEffect(() => {
console.log('Component loaded !!');
}, []);
return (
<>
<h3>
Welcome Employees !!
</h3>
</>
)
}
For changing the route dynamically, we'll add a button to the employees' page.
<button onClick={handleClick}>
Click
</button>
In the button click handler method, we'll change the route by making use of the useRouter
. So, first import the useRouter
hook.
import { useRouter } from 'next/router'
And using useRouter
you can push the route url. Here is how the handleClick
method looks:
const router = useRouter();
const handleClick = () => {
router.push("/employees?empId=1", "", { shallow: true });
}
In the above code, in router.push
we are passing in the shallow
option as true
. Using shallow routing simply changes the route url as stated without reloading the component.
To detect changes in the route URL you can add useEffect
hook to router.query
and add the required logic. Here is the complete pages/employees/index.tsx
file:
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react';
export default function Employees() {
const router = useRouter();
const [empId, setEmpId] = useState<any>(0);
const handleClick = () => {
router.push("/employees?empId=1", "", { shallow: true });
}
useEffect(() => {
const { empId = 0 } = router?.query;
setEmpId(empId);
}, [router.query]);
useEffect(() => {
console.log('Component loaded !!');
}, []);
return (
<>
<h3>
Welcome
EmployeeId: {empId}
</h3>
<button onClick={handleClick}>
Click
</button>
</>
)
}
In the above code, on the button click the router url changes from /employees
to /employees?emplId=1
as shallow routing. Hence the component doesn't reload. Hence we are using the useEffect
hook change on router.query
to detect route changes and display the changed empId
in the rendered page.
Wrapping it up
Next.js provides a number of ways to route your pages. You can use index routing, static routing, dynamic routing, or shallow routing as per your needs for routing pages.