Tuesday, 28 February, 2023 UTC


Summary

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.