How to implement routing in a Next.js app?
Implementing routing in a Next.js application is a straightforward process, thanks to the framework's file-based routing system. Next.js, a popular React framework, offers both static generation (SSG) and server-side rendering (SSR) capabilities for building highly performant and scalable web applications. In this article, we'll delve into how to implement routing in a Next.js app, covering the basics, dynamic routes, nested routes, and programmatic navigation.
Prerequisites
Before diving into routing, ensure you have the following prerequisites:
- Node.js installed on your machine.
- Basic knowledge of React.
- A new or existing Next.js project. If you're starting a new project, create one by running
npx create-next-app@latest
.
Basic Routing
Next.js uses a file-based routing system, where the routes are determined by the file structure within the pages
directory. Here's how to implement basic routing:
-
Creating Pages: Every
.js
,.jsx
,.ts
, or.tsx
file inside thepages
directory (exceptpages/_app.js
andpages/_document.js
) automatically becomes a route.For example, creating
pages/about.js
makes the/about
route available. -
Linking Pages: Use the
Link
component fromnext/link
to navigate between pages without a full page reload.Example:
import Link from "next/link"; function HomePage() { return ( <div> <h1>Welcome to the Home Page</h1> <Link href="/about"> <a>About Us</a> </Link> </div> ); } export default HomePage;
Dynamic Routing
To create a dynamic route, add square brackets to a page name in the pages
directory, like [id].js
. This allows you to capture the value of the id
parameter within your page.
-
Creating a Dynamic Route: Create a file named
[id].js
inside thepages/posts
directory for a dynamic post route. -
Fetching Data: Use
getStaticProps
andgetStaticPaths
(for SSG) orgetServerSideProps
(for SSR) to fetch data based on the dynamic parameter.Example in
pages/posts/[id].js
:import { useRouter } from "next/router"; const Post = () => { const router = useRouter(); const { id } = router.query; return <p>Post: {id}</p>; }; export default Post;
Nested Routes
To create nested routes, you simply structure your pages
directory in the way you want your URL to be structured.
For example, to create a route like /blog/2021/first-post
, your file structure would be:
blog/
2021/
first-post.js
Programmatic Navigation
Besides using the Link
component, you can also navigate programmatically using the useRouter
hook from next/router
.
Example:
import { useRouter } from "next/router";
function HomePage() {
const router = useRouter();
const navigateToAbout = () => {
router.push("/about");
};
return (
<div>
<button onClick={navigateToAbout}>Go to About Page</button>
</div>
);
}
export default HomePage;
Building upon the foundational knowledge of routing in Next.js, let's explore some advanced routing concepts including fallback loading states in dynamic routes, catch-all routes, and optimizing user experience with shallow routing.
Fallback Loading States in Dynamic Routes
When you're generating dynamic routes using getStaticPaths
with { fallback: true }
, you may encounter situations where the page hasn't been generated yet. In such cases, Next.js serves a fallback version of the page initially, which is perfect for rendering loading placeholders.
Implementing a Fallback State:
In your dynamic page, you can use the isFallback
property from useRouter
to determine if the fallback is being displayed:
import { useRouter } from "next/router";
const Post = () => {
const router = useRouter();
if (router.isFallback) {
return <div>Loading...</div>;
}
// Your normal page rendering
return <div>Post content</div>;
};
export default Post;
Catch-All Routes
Catch-all routes allow you to match paths that have multiple segments. You might use this for a CMS-like experience where you don't know the exact depth of your URLs.
Creating a Catch-All Route:
To create a catch-all route, add three dots (...
) inside the brackets. For example, pages/posts/[...slug].js
will match /posts/a
, /posts/a/b
, /posts/a/b/c
, and so on.
Accessing Parameters:
In your page component, you can access the catch-all parameters as an array under the key you've named in the file. For the above example, the key would be slug
.
import { useRouter } from "next/router";
const Post = () => {
const router = useRouter();
const { slug } = router.query; // slug is an array
return <div>Slug: {slug.join("/")}</div>;
};
export default Post;
Shallow Routing
Shallow routing allows you to change the URL without running data fetching methods again, that means you can update the path without triggering getStaticProps
, getServerSideProps
, or getInitialProps
to re-run. This is particularly useful for filtering or pagination where the page data doesn't need to be fetched again.
Using Shallow Routing:
To enable shallow routing, pass the shallow: true
option to router.push
or router.replace
.
const { router } = useRouter();
// Assuming the current path is `/events?counter=0`
const handleClick = () => {
/*
This will update the URL without re-running data fetching methods
on the page, which means the page/content remains unchanged but the URL changes.
*/
router.push("/events?counter=1", undefined, { shallow: true });
};
Optimizing Navigation with Prefetching
Next.js automatically prefetches pages linked with the Link
component that are visible in the viewport. This makes navigation to these pages feel instant. However, you can also manually prefetch pages if needed, for instance, in response to a user's action.
Manually Prefetching a Page:
You can use the router.prefetch
method to prefetch a page programmatically.
const { router } = useRouter();
const handleMouseOver = () => {
// Prefetch the about page
router.prefetch("/about");
};
return (
<div onMouseOver={handleMouseOver}>Hover me to prefetch the About page!</div>
);
Conclusion
Advanced routing techniques in Next.js enhance the flexibility and user experience of your application. By leveraging fallback loading states, you can improve the loading experience for your users. Catch-all routes offer unmatched flexibility in handling a wide range of URL structures. Shallow routing and manual prefetching are powerful tools for optimizing your application's interactivity and responsiveness. With these advanced concepts, you can build dynamic and highly efficient Next.js applications that cater to complex routing requirements.
Routing in Next.js is intuitive and powerful, leveraging a file-based system for simplicity and supporting advanced patterns like dynamic and nested routes. By understanding the basics of routing and navigation in Next.js, you can build complex and highly interactive web applications. Remember, the key to mastering Next.js routing lies in organizing your pages
directory effectively and leveraging the framework's built-in hooks and components for navigation.