Next.js is a React framework that provides a robust set of features for building server-side rendered and static web applications. It simplifies the process of creating universal apps — applications that can render both on the client and the server. But what truly sets Next.js apart is its file-system based routing system, which is unlike anything you'll find in traditional React apps.
Routing is an integral part of any web application. It's the feature that allows users to navigate through different parts of an application. In a traditional React application, routing is handled through libraries like react-router-dom. However, Next.js approaches routing in a unique way that offers developers more flexibility and control.
In Next.js, the routing system is based on the file system. This means that the app directory in your Next.js application directly maps to the available routes. For example, if you have a file named about.js in your app directory, it will be accessible via the /about route in your application.
1 // app/about.js 2 export default function About() { 3 return ( 4 <div> 5 <h1>About Page</h1> 6 </div> 7 ) 8 } 9
In the above example, I've created a simple React component for the About page. When you navigate to /about in your browser, Next.js will automatically render this component.
This approach to routing is incredibly intuitive and eliminates the need for a separate routing configuration. However, the real power of Next.js routing comes from its support for dynamic routes.
Dynamic routes allow you to create routes that can handle a variety of URL paths. For instance, if you're building a blog, you might have a app/posts/[id].js file to handle individual blog posts.
1 // app/posts/[id].js 2 import { useRouter } from 'next/router' 3 4 export default function Post() { 5 const router = useRouter() 6 const { id } = router.query 7 8 return ( 9 <div> 10 <h1>Post: {id}</h1> 11 </div> 12 ) 13 } 14
In the above example, the [id] in the file name indicates that this is a dynamic route. The useRouter hook is used to access the router object, which contains the query object. The query object contains key-value pairs of the dynamic route parameters. In this case, the id of the post.
Before we delve into the specifics of Next.js routing, let's first set up a new Next.js project. This will give us a sandbox where we can explore various routing features in detail.
Creating a new Next.js project is straightforward. You can use create-next-app, which sets up everything automatically for you. To create a new project, open your terminal, navigate to the folder where you want to create your project and run the following command:
1 npx create-next-app@latest my-app 2
In the above example, my-app is the name of your new application. You can replace it with any name you prefer.
Once you've created your Next.js project, you'll notice a directory named app. This directory is one of the most important in a Next.js application because it's where you'll define your routes.
In Next.js, every file inside the app directory becomes a route automatically. For example, if you create a file named contact.js inside the app directory, it will be accessible via the /contact URL path.
1 // app/contact.js 2 export default function Contact() { 3 return ( 4 <div> 5 <h1>Contact Page</h1> 6 </div> 7 ) 8 } 9
In the above example, I've created a simple React component for the Contact page. When you navigate to /contact in your browser, Next.js will automatically render this component.
Now that you understand the role of the app directory, let's create our first route. Inside the app directory, create a new file named about.js.
In about.js, we'll define a simple React component that will serve as the content for our About page.
1 // **app**/about.js 2 export default function About() { 3 return ( 4 <div> 5 <h1>About Page</h1> 6 </div> 7 ) 8 } 9
And that's it! You've just created your first route in Next.js. If you navigate to http://localhost:3000/about in your browser, you'll see your new About page.
Now that we have a basic understanding of how Next.js routing works, let's dive deeper. In this section, we'll explore the Link component and the useRouter hook, two fundamental aspects of the Next.js routing system.
The Link component is a built-in component in Next.js that allows you to create links to different app in your application. It's similar to the <a>
tag in HTML, but it's designed to work seamlessly with the Next.js routing system.
To create a link, you'll need to import the Link component from next/link. Here's an example of how to create a link to the home page:
1 // Importing the Link component 2 import Link from 'next/link' 3 4 export default function Navigation() { 5 return ( 6 <nav> 7 <Link href="/"> 8 <a>Home</a> 9 </Link> 10 </nav> 11 ) 12 } 13
In the above example, the href prop on the Link component is set to the URL path you want to link to. The Link component doesn't render any HTML on its own, it just provides the routing functionality. That's why you need to wrap it around a <a>
tag or any other component that can trigger a navigation event.
One of the great features of the Link component is its ability to prefetch pages. By default, Next.js automatically prefetches the code for a page when a Link to that page appears in the viewport. This means that the page is ready to be instantly displayed when a user clicks on the link.
This feature can significantly improve the performance of your application by reducing the load time when navigating between different pages.
While the Link component is great for declarative navigation, sometimes you need a bit more control. This is where the useRouter hook comes in.
The useRouter hook allows you to access the router object inside any React component. The router object contains various properties and methods that you can use to control the routing of your application programmatically.
Here's an example of how to use the useRouter hook:
1 // Importing the useRouter hook 2 import { useRouter } from 'next/router' 3 4 export default function Page() { 5 const router = useRouter() 6 7 return ( 8 <div> 9 <h1>{router.pathname}</h1> 10 </div> 11 ) 12 } 13
In the above example, I'm using the useRouter hook to access the router object. Then, I'm displaying the current URL path using the router.pathname property.
In addition to accessing the current route information, you can also use the router object to navigate programmatically. This can be useful in situations where you need to redirect the user after they perform a certain action, like submitting a form.
Here's an example of how to navigate to a different page programmatically:
1 // Importing the useRouter hook 2 3 export default function Page() { 4 const router = useRouter() 5 6 const handleButtonClick = () => { 7 router.push('/about') 8 } 9 10 return ( 11 <div> 12 <button onClick={handleButtonClick}> 13 Go to About Page 14 </button> 15 </div> 16 ) 17 } 18
In the above example, I'm using the router.push method to navigate to the About page when the button is clicked.
Dynamic routing is a powerful feature in Next.js that allows you to create routes with dynamic path segments. These dynamic segments can then be used to fetch and display data based on the current route.
In Next.js, a dynamic route is a page in the app directory that has its filename wrapped in square brackets ([]). For example, a file named app/posts/[id].js would match any route in the form of /posts/1, /posts/2, /posts/abc, and so on.
The value inside the square brackets (id in this case) is a dynamic route parameter. You can use this parameter to fetch and display data specific to each route.
Creating a dynamic route in Next.js is as simple as creating a new file in the app directory with its filename wrapped in square brackets. Here's an example:
1 // app/posts/[id].js 2 import { useRouter } from 'next/router' 3 4 export default function Post() { 5 const router = useRouter() 6 const { id } = router.query 7 8 return ( 9 <div> 10 <h1>Post: {id}</h1> 11 </div> 12 ) 13 } 14
In the above example, I'm using the useRouter hook to access the router object. The router.query object contains key-value pairs of all the dynamic route parameters. In this case, I'm destructuring the id from router.query and displaying it in the component.
In a real-world application, you'd likely use the dynamic route parameter to fetch data from an API. Here's an example of how you might do this:
1 // app/posts/[id].js 2 import { useRouter } from 'next/router' 3 import useSWR from 'swr' 4 5 export default function Post() { 6 const router = useRouter() 7 const { id } = router.query 8 9 const { data, error } = useSWR(`/api/posts/${id}`, fetch) 10 11 if (error) return <div>Failed to load post</div> 12 if (!data) return <div>Loading...</div> 13 14 return ( 15 <div> 16 <h1>{data.title}</h1> 17 <p>{data.content}</p> 18 </div> 19 ) 20 } 21
In the above example, I'm using the useSWR hook from the SWR library to fetch data from an API. The API URL includes the id from the dynamic route parameter.
This is just a basic example. In a real-world application, you'd likely have more complex data fetching logic and error handling.
Next.js provides several advanced routing techniques that give you even more control and flexibility over your application's routing. In this section, we'll explore nested routing, catch-all routes, and optional dynamic routes.
Nested routing allows you to create routes that reflect a hierarchical structure in your application. This can be useful for cases where certain pages are sub-sections of a larger section.
To create a nested route, you simply create a folder inside the app directory and then create the sub-route as a file inside that folder. For example, if you wanted to create a route for a user's settings page, you might create a user folder and a settings.js file inside it:
1 // app/user/settings.js 2 export default function Settings() { 3 return ( 4 <div> 5 <h1>User Settings</h1> 6 </div> 7 ) 8 } 9
In the above example, the Settings page would be accessible at the /user/settings URL path.
Catch-all routes allow you to match routes that don't correspond to any file in the app directory. This can be useful for displaying a 404 page or for handling dynamic routes with multiple parameters.
To create a catch-all route, you add [...param] to a file name in the app directory. The param inside the brackets is the name of the array that will hold your route parameters.
Here's an example of a catch-all route:
1 // app/posts/[...id].js 2 import { useRouter } from 'next/router' 3 4 export default function Post() { 5 const router = useRouter() 6 const { id } = router.query 7 8 return ( 9 <div> 10 <h1>Post: {id.join('/')}</h1> 11 </div> 12 ) 13 } 14
In the above example, the id parameter is an array that contains all the route segments after /posts. This allows you to handle routes like /posts/1/2/3 and /posts/a/b/c.
Optional dynamic routes are a new feature in Next.js 9.3. They allow you to create routes that work with or without a dynamic route parameter.
To create an optional dynamic route, you add brackets around the parameter in the file name, and then add a question mark (?) at the end. Here's an example:
1 // app/posts/[id].js 2 import { useRouter } from 'next/router' 3 4 export default function Post() { 5 const router = useRouter() 6 const { id = 'default' } = router.query 7 8 return ( 9 <div> 10 <h1>Post: {id}</h1> 11 </div> 12 ) 13 } 14
In the above example, the id parameter is optional. If it's not provided in the URL, the id will default to 'default'.
Throughout this guide, we've journeyed through the world of Next.js routing, a key feature that sets this React framework apart. We've started from the ground up, learning how to set up a Next.js project and create static routes. We've also explored how to leverage the Link component and useRouter hook for seamless navigation within our applications.
We've seen the power of dynamic routing in Next.js, a feature that provides us with the ability to handle a variety of URL paths, making our applications more flexible and robust. Furthermore, we've delved into advanced routing techniques such as nested routing, catch-all routes, and optional dynamic routes, which offer even greater control over our application's routing structure.
The routing system in Next.js is more than just a tool; it's a fundamental part of building modern web applications. Mastering Next.js routing will undoubtedly elevate your web development skills. As you continue to explore and experiment with Next.js, you'll find its routing system to be an indispensable compass, guiding you through the intricate landscape of modern web development.
Tired of manually designing screens, coding on weekends, and technical debt? Let DhiWise handle it for you!
You can build an e-commerce store, healthcare app, portfolio, blogging website, social media or admin panel right away. Use our library of 40+ pre-built free templates to create your first application using DhiWise.