Custom 404 Page in Next.js 15
Used in this guide:Intro
In any production-ready website, a clean and user-friendly 404 page (Not Found) is essential. It keeps users engaged, reassures them that your site is intentional and polished, and gently redirects them when they land on a broken or outdated link.
Thankfully, in Next.js 15 App Router, setting up a custom 404 page is incredibly simple — all you have to do is create a file called not-found.tsx in the app directory.
Next.js will automatically render it for any route that doesn't match an existing page.
In this tutorial, you'll learn how to:
- Create a custom 404 page with a friendly design
- Add navigation back to the home page
- Reuse layout elements like a Navbar
Create a simple Navbar
This is a simple navigation bar that we include globally via layout.tsx. It's optional for the 404 page but helps maintain consistent UI across your site.
import Link from "next/link";
const links = [
{ name: "Home", path: "/" },
{ name: "Services", path: "services" },
{ name: "About", path: "about" },
{ name: "Contact", path: "contact" },
]
const Navbar = () => {
return (
<div className="flex justify-between items-center w-full max-w-[1280px] mx-auto px-[24px] py-[40px]">
<span className="font-bold text-xl">LOGO</span>
<div className="flex gap-[24px] items-center">
{links.map((item, index) => (
<Link key={index} href={item.path}>
<span className="hover:text-teal-500">{item.name}</span>
</Link>
))}
</div>
</div>
)
}
export default Navbar;This component defines a basic top navigation bar with links to common pages like Home, Services, About, and Contact. We'll render this in our root layout so it's shared across all pages, including the custom 404 page.
Updating the Root Layout
This wraps all your routes, including the 404 page, with shared UI components like your Navbar.
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import Navbar from "./components/Navbar";
const inter = Inter({
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Tut | Custom Error Page",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${inter.className} antialiased`}
>
<Navbar/>
{children}
</body>
</html>
);
}This layout file sets up your site’s global structure. It includes the <Navbar /> at the top and renders all pages including the custom 404 inside the body. By putting the Navbar here, you ensure consistent navigation no matter what page the user lands on.
Defining the 404 Page
This file is all you need to define a custom 404 page in the App Router.
import Link from "next/link";
export default function NotFound() {
return (
<main className="flex flex-col gap-[24px] w-full max-w-[600px] mx-auto p-[24px] mt-[40px] items-center text-center">
<h1 className="text-3xl">404 – Page Not Found</h1>
<p className="font-light opacity-70">We couldn’t find what you were looking for. It might’ve been removed, renamed, or never existed. Don’t worry, you can always start fresh from the homepage.</p>
<Link href="/">
<button className="py-2 px-4 bg-teal-400 text-slate-700 rounded-md cursor-pointer">
Back to Home
</button>
</Link>
</main>
)
}This is your actual 404 page. By naming it not-found.tsx and placing it in the app/ directory, Next.js will use it automatically when users hit a route that doesn’t exist. It contains a short message, a description, and a button linking back to the homepage.
Final Result
Now when a user visits a non-existent route (e.g. /unknown-page), they'll see your custom 404 page together with your site's branding (Navbar) and a helpful way to get back on track.
Pro Tip: Route-Specific 404 Pages
Next.js 15 allows you to define custom 404 pages for specific routes, not just a global one. Let’s say you have a directory like:
app/(pages)/blog/You can create a route-specific 404 page just by adding:
app/(pages)/blog/not-found.tsxThis file will only be used when a user navigates to a non-existent route inside /blog, and it will override the global app/not-found.tsx.
Why this is useful:
- Create different 404 experiences per section of your site (e.g. blog vs dashboard)
- Show helpful context-specific links or content suggestions
- Keep your global 404 page minimal, and tailor more detailed fallbacks by route