Routing in Next.js - what is it and how to create routing?

Blog image - Routing w Next.js - na czym polega i jak tworzyć routy?

15/12/2023

9 min

Bartosz Lewandowski

Routing in Next.js - what is it and how to create routing?

15/12/2023

9 min

Bartosz Lewandowski

Blog image - Routing w Next.js - na czym polega i jak tworzyć routy?

Table of Contents

  1. Creating Routes
  2. File Structure in the app Folder
  3. Navigation
  4. Dynamic Paths (dynamic routes)
  5. Summary

Also read: What is Vercel and is it worth choosing? - A few words from an expert

Routing is one of the most important aspects of any web application. In this article, we will take a close look at how routing works in Next.js (app router), the new version of the popular framework. Have you ever wondered how clicking a link takes you to another part of a website without having to reload the entire page? That's the magic of routing! In Next.js, routing is simpler than ever, but what exactly does that mean for you as an application developer?

Creating Routes

Basic routes: Every JavaScript, TypeScript, JSX, or TSX file in the app folder creates a route. For example, the file app/about/page.tsx corresponds to the route /about.

Dynamic parameters: To create a route with dynamic parameters, use square brackets in the file name. For example, app/posts/[id]/page.tsx will correspond to a route like /posts/123.

Nested routes: You can create nested routes by placing files in subdirectories. For example, app/products/electronics/page.tsx corresponds to the route /products/electronics.

File Structure in the app Folder

In Next.js 13, the "App Router" represents a significant change in how developers organize and manage components and routes in their applications. This new concept brings greater flexibility and a more modular architecture, allowing for better management of the application's structure and its routes.

Layout

The layout.tsx file is used to define a common layout for a group of routes. It can be used to specify elements that will be common to many pages, such as a header, footer, sidebar, navigation, etc.

Characteristics:

  1. Placement: It is placed in the app directory or its subdirectories to define layouts for specific sections of the application.
  2. Nesting: You can create nested layouts. For example, if you have layout.tsx in app/products, it will be applied to all pages in app/products and its subdirectories.

Example:

import React from "react";
 
const Layout = ({ children }) => {
	return (
		<div>
			<header>Header Content</header>
			<main>{children}</main>
			<footer>Footer Content</footer>
		</div>
	);
};
export default Layout;
 

Template

The template.tsx file in Next.js 13 serves a role similar to a layout (layout.tsx), but is used in a slightly different way. Its main function is to wrap each child (child layout or page) with a unique instance, meaning it is recreated on each navigation.

Characteristics:

  1. New instance on navigation: Every time a user navigates between routes using the same state, template.tsx is remounted. This means DOM elements are recreated, state is not preserved, and effects are resynchronized.
  2. Using useEffect and useState: It is an ideal place for functions that rely on useEffect (e.g., logging page views) and useState (e.g., feedback forms specific to each page).

Example:

import React from "react";
 
const Template = ({ title, content }) => {
	return (
		<div>
			<h2>{title}</h2>
			<p>{content}</p>
		</div>
	);
};
export default Template;

Page

The page.tsx file forms the basis for each individual page in a Next.js application.

Characteristics:

  1. URL Structure: The name and location of the page.tsx file directly define the URL path for that page.
  2. Page Logic: It can contain local state, event handling, dynamic imports, and other logic specific to that page.

Example:

import React from "react";
 
const ContactPage = () => {
	return (
		<div>
			<h1>Contact</h1>
			<p>Contact us using the form below.</p>
		</div>
	);
}
export default ContactPage;

Loading

The loading.tsx file defines the component displayed while data or page content is loading.

Characteristics:

  1. Data Loading: Provides a graphical representation of data loading, enhancing the user experience.
  2. Versatility: Can be used in various contexts, e.g., when loading a page, data, images.

Example:

import React from "react";
 
const Loading =()=> {
	return (
		<div className="loading-container">
			<p>Loading, please wait...</p>
		</div>
	);
}
 
export default Loading;

Not Found

The not-found.tsx file is used to define a component displayed when a user tries to access a page that does not exist (404 error).

Characteristics:

  1. User Communication: Informs the user that the page they are looking for was not found.
  2. Friendly Interface: Typically contains links back to the homepage or other main sections of the site.

Example

import React from "react";
import Link from "next/link";
 
const NotFound = () =>{
	return (
		<div className="not-found-container">
			<h1>404 - Page Not Found</h1>
			<p>We can't find the page you're looking for.</p>
			<Link href="/">Return to Homepage</Link>
		</div>
	);
}
 
export default NotFound;

Error

The error.tsx file is used to create a component displayed in the event of an error in the application.

Characteristics:

  1. Error Handling: Can handle various types of errors, including server errors or application errors.
  2. Informing the User: Contains information about the error and possible suggestions for the user.

Example

import React from "react";
 
const  ErrorPage =({ statusCode }) =>{
	return (
		<div className="error-container">
			<h1>Error</h1>
			<p>An error occurred{statusCode ? `: ${statusCode}` : ""}.</p>
			<p>We apologize for the inconvenience.</p>
		</div>
	);
}
 
export default ErrorPage;

In Next.js, there are two ways to navigate between routes:

  1. Using the <Link> component
  2. Using the useRouter hook

Below, I'll discuss how to use <Link> and useRouter(), and explain how navigation works.

is a built-in component that extends the HTML <a> tag, offering prefetching and client-side navigation between routes. It is the primary way to navigate between routes in Next.js. You can use it by importing it from next/link and passing the href prop to the component:

import Link from "next/link";
 
const Page =()=> {
	return <Link href="/dashboard">Dashboard</Link>;
}
 
export default Page;

Examples

Linking to dynamic segments: You can use template literals and interpolation to generate a list of links. For example, to generate a list of blog posts:

import Link from "next/link";
 
export default function Posts({ posts }) {
	return (
		<ul>
			{posts.map((post) => (
				<li key={post.id}>
					<Link href={`/blog/${post.slug}`}>{post.title}</Link>
				</li>
			))}
		</ul>
	);
}

Checking active links: You can use usePathname() to determine if a link is active. For example, to add a class to an active link, you can check if the current pathname matches the link's href:

import { usePathname } from "next/navigation";
import Link from "next/link";
export function LinkComponent() {
	const pathname = usePathname();
	return (
		<nav>
			<ul>
				<li>
					<Link className={`${pathname === "/" ? "active" : ""}`} href="/">
						Home
					</Link>
				</li>
			</ul>
		</nav>
	);
}
 

Scrolling to id: You may want to scroll to a specific id when navigating. To do this, you can append a # hash to the URL

<Link href="/marketplace#milk">Milk</Link>

Disabling scrolling: If you want to disable this behavior, you can pass scroll={false} to the <Link> component

<Link href="/marketplace" scroll={false}>Marketplace</Link>

Hook useRouter()

In Next.js, there is the possibility of directly using the Router, which can be useful in specific situations, even though in most cases using the <Link> component is preferred. Consider using the router in the following circumstances:

  1. Automatic redirection: After performing an asynchronous operation, such as an API request, it may be necessary to automatically redirect the user to another subpage.
  2. Changing the address for unauthenticated users: In situations where an unauthenticated user tries to access restricted resources, the router can be used to change the page address to a more appropriate one.
  3. Navigation after login: After a successful login process, the router can be used to redirect the user back to the page from which they started the login process. The router object contains several useful functions: push, replace, refresh, prefetch, back, forward

Example:

import { useRouter } from "next/navigation";
export default function Page() {
	const router = useRouter();
 
	const navigateToMarketplace = () => {
		router.push("/marketplace");
	};
 
	return <button onClick={navigateToMarketplace}>Marketplace</button>;
}

Hook useParams()

This hook provides us with an object containing dynamic parameters, which are essentially equivalent to what we find in params passed to the page.tsx component.

Example:

import { useParams } from "react-router-dom";
 
function ProductPage() {
	const { productId } = useParams();
	return <div>Product ID: {productId}</div>;
}

Hook useSearchParams()

This functionality provides something similar to searchParams from page.tsx, but with a fundamental difference. Instead of a regular object, we receive an instance of URLSearchParams, or more precisely, ReadonlyURLSearchParams.

Example:

import { useSearchParams } from "react-router-dom";
function SearchPage() {
	const [searchParams] = useSearchParams();
	return <div>Search term: {searchParams.get("query")}</div>;
}

If the URL is /search?query=phone, the SearchPage component will display "Search term: phone".

Hook usePathname()

Returns the current pathname. Example:

import { usePathname } from "react-router-dom";
function Navigation() {
	const pathname = usePathname();
	return <div>Current path: {pathname}</div>;
}

If the URL is /about, the Navigation component will display "Current path: /about".

Hook useSelectedLayoutSegment()

Allows reading the path of the page file at a deeper level than the component in which it was called. Although it sounds complex, the example below should clarify it. Using this hook makes sense only in layout.tsx files.

Example:

import { useSelectedLayoutSegment } from "react-router-dom";
 
function DashboardLayout() {
	const segment = useSelectedLayoutSegment();
	return <div>Current segment: {segment}</div>;
}

In this example, if the user is on the /dashboard/settings page, DashboardLayout will display "Current segment: settings".

Hook useSelectedLayoutSegments()

Works similarly to useSelectedLayoutSegment, but returns an array of paths, hence the plural in the name. Let's look at an example for better understanding: Example:

import { useSelectedLayoutSegments } from "react-router-dom";
 
function Breadcrumbs() {
	const segments = useSelectedLayoutSegments();
	return <div>Path: {segments.join(" / ")}</div>;
}

If the URL is /dashboard/settings/profile, Breadcrumbs will display "Path: dashboard / settings / profile".

Dynamic Paths (dynamic routes)

Next.js, a popular framework for building applications, offers a powerful dynamic routing feature that opens up new possibilities for developers. Dynamic paths in Next.js allow for creating more interactive and scalable web applications.

What are dynamic paths?

Dynamic paths in Next.js allow for creating URLs that adapt to user input or specific application requirements. This enables the creation of more flexible and user-friendly applications.

Convention

A dynamic segment is created by enclosing the folder name in square brackets: [folderName]. For example, [id] or [slug].

These dynamic segments are passed as the params prop to layout, page, route, and generateMetadata functions.

Example: For example, a blog may contain the path app/blog/[slug]/page.js, where [slug] is a dynamic segment for blog posts.

export default function Page({ params }: { params: { slug: string } }) {
	return <div>My Post: {params.slug}</div>;
}

Static Pages

The generateStaticParams function is used for statically generating routes at build time.

// app/blog/[slug]/page.tsx

export async function generateStaticParams() {
	const users = await fetch("https://.../users").then((res) => res.json());
	return users.map((user) => ({ slug: user.id }));
}
 

Applications of Dynamic Paths

  1. User profile pages: As mentioned earlier, dynamic paths are ideal for creating profile pages where each user has a unique URL based on their ID.
  2. Blogs and articles: Similarly, in news sites or blogs, dynamic paths can be used to generate individual pages for each article or post.
  3. Online stores: In e-commerce, dynamic paths allow for creating separate pages for each product, where the path includes a unique identifier or product name.

Summary

In this post, I focus on routing in Next.js, an important aspect of web application development. I discussed the basics of creating different types of routes in Next.js, including standard, dynamic, and nested routes, as well as the file structure in the app folder. I included detailed information about components like layout.tsx, template.tsx, page.tsx, and handling various situations like loading pages, errors, or not found pages. Additionally, the guide presents different navigation methods, including using the <Link> component, various hooks, and dynamic paths. Practical code examples are included for each topic to better understand their application.

Phew... We've got that covered. Still hungry for knowledge? It's time to dive into the very important topic of rendering. But don't worry, because in the article Rendering in Next.js - server and client components in a nutshell, we covered everything in detail! Check out the FAQ section and see you there! :)

Frequently Asked Questions

  1. What is routing in Next.js? Routing in Next.js is a way of managing navigation in an application, allowing different components to be loaded without reloading the entire page.
  2. How do you create basic routes in Next.js? Basic routes are created by placing JavaScript, TypeScript, JSX, or TSX files in the app folder. The name and location of the file directly define the URL path.
  3. What are dynamic paths in Next.js? Dynamic paths are those that can change based on user input or specific application requirements, enabling the creation of more flexible routes.
  4. How does navigation work in Next.js? Navigation in Next.js can be done using the <Link> component for client-side navigation or through hooks like useRouter for more advanced navigation use cases.
  5. What are the differences between layout.tsx, template.tsx, and page.tsx? layout.tsx defines a common layout for a group of routes, template.tsx creates a unique instance for each child on navigation, and page.tsx forms the basis for each individual page in the application.
  6. How are errors and not-found pages handled in Next.js? Special files like error.tsx and not-found.tsx are used to define components displayed in the event of errors or when a page is not found.
Bartosz Lewandowski

Bartosz Lewandowski

CEO

Newsletter