07 April, 2024
4 min
How to Create a Custom 404 Error Page in Nextjs 13
Creating a custom 404 page in Next.js isn't as straightforward and restricted as it used to be, especially with the recent changes introduced in Next.js 13, the process has become more flexible and powerful, allowing for a more nuanced approach to handling errors and improving user experience.
In this article, we'll explore how to create a custom 404 error page that catches any unmatched URLs in your next app and then I'll show you how you can set up this error page in nested routes to display a different error page from the one on the root using the not-found.js file.
If you want to learn how to build a custom 404 page in older versions of nextjs, check out my other article on how to create a custom 404 page in nextjs.
Unlike next 12 that uses a static default 404 page, next 13 makes use of a file called not-found.js.
This file is a server component by default, and it supports data fetching.
To get started, we'll first explore how to setup this 404 page to handle all unmatched URLs in your app:
The root app/not-found.js file handles all unmatched URLs for your whole application; this means users who visits a URL that does not exist on your app will be shown the UI exported by the app/not-found.js file.
To get started, create a not-found.js file inside your app directory. Here's a typical markup that can be used in this file:
import Link from "next/link";
export default function NotFound() {
return (
<main>
<h1>Error 404 😥</h1>
<p>Oops! This page was not found</p>
<Link href="/">
Return Home
</Link>
</main>
);
}
You can style this component just like every other component in nextjs. Above, I'm simply adding some styles via the global.css file.
Once added, try visiting any route that does not exist within your app and it should redirect to this component.
Aside from creating an error route, the not-found.js file also provides a built-in function called notFound() that triggers the not-found component when it is called within a route segment. Let's discuss that in the next section.
This works by importing the notFound module from "next/navigation" and calling the function within the dynamic segment. Let's say you have a dynamic route that results in individual blog posts: blog/a, blog/b, you can catch errors within the dynamic route and display a separate not-found.js file from the one defined in the root.
First, we have a blog directory that contains a dynamic route (slug) for individual posts, and then the not-found.js file that should be triggered for only the nested slug component.
Here's the markup for the nested not-found.js file:
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
export default function NotFoundComponent() {
const path = usePathname();
return (
<main className="container">
<h1>{`"${path}"`} Not Found 📖</h1>
<p>Oops! We could not find this post.</p>
<Link href="/blog" className="button">
Return To Blog
</Link>
</main>
);
}
To demonstrate how this will work, here's a list of links that should route to individual posts via the [slug] parameter.
import Link from "next/link";
export default function Blog() {
return (
<main>
<h1>Blog</h1>
<Link href="/blog/a">Post A</Link>
<Link href="/blog/b">Post B</Link>
<Link href="/blog/cc">Post C</Link>
</main>
);
}
Within the dynamic component, you can import the notFound module from "next/navigation" and call the function based on any condition of your choosing:
import Link from "next/link";
import { notFound } from "next/navigation";
export default function Post({ params }: { params: { slug: string } }) {
if (params.slug.length > 1) {
notFound();
}
return (
<div>
<h1>Post Slug: {params.slug} 📖</h1>
<p>
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Delectus,
dicta.
</p>
<Link href="/blog">
Return To Blog
</Link>
</div>
);
}
In the example above, the function is been called whenever the post slug length is greater than 1. Since /blog/cc matches this condition, when you navigate to its URL, you will get the not-found error.
his is just an example to showcase how the notFound function works. A more valid use case for calling this function would be when a post is missing or unavailable.