A well-designed 404 page can make a big difference in user retention.
Instead of showing a default system message, you can build a custom 404 page that matches your brand and helps users navigate your site more easily.
In this guide, we’ll explore how Next.js handles 404 pages—and how you can create one that’s user-friendly, SEO-optimized, and visually appealing.
Understanding the Default 404 Page
By default, Next.js automatically provides a basic 404 page when a route doesn’t exist.
If you navigate to a non-existent URL, e.g.:
http://localhost:3000/this-page-does-not-exist
You’ll see a plain system message like:
404 - This page could not be found.
That’s functional, but not user-friendly. Let’s fix that by creating our own!
Step 1: Create a Custom 404 Page
If your app uses the app/ directory, create a file named not-found.tsx inside any route segment where you want to customize the 404 behavior.
For a global 404 page, create:
app/not-found.tsx
Now add this code:
// app/not-found.tsx
import Link from "next/link";
export default function NotFound() {
return (
<main className="flex flex-col items-center justify-center min-h-screen text-center">
<h1 className="text-5xl font-bold mb-4">404 - Page Not Found</h1>
<p className="text-gray-500 mb-6">
Oops! The page you're looking for doesn't exist or has been moved.
</p>
<Link
href="/"
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition"
>
Go Back Home
</Link>
</main>
);
}
Next.js will automatically show this component for any non-existing route.
Step 2: Add Styling and Branding
To make your 404 page more engaging, you can include your brand colors, illustrations, or animations.
Example with Tailwind CSS:
// app/not-found.tsx
import Link from "next/link";
export default function NotFound() {
return (
<section className="flex flex-col items-center justify-center min-h-screen bg-gray-50">
<h1 className="text-6xl font-bold text-blue-600">404</h1>
<p className="mt-4 text-lg text-gray-600">
Sorry, we couldn't find that page.
</p>
<Link
href="/"
className="mt-6 px-6 py-3 bg-blue-600 text-white rounded-full hover:bg-blue-700 transition"
>
Return Home
</Link>
</section>
);
}
Step 3: Add Motion and Animation (Optional)
For an extra polished look, you can add subtle animations using Framer Motion:
"use client";
import { motion } from "motion/react";
import Link from "next/link";
export default function NotFound() {
return (
<main className="flex flex-col items-center justify-center min-h-screen">
<motion.div
initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, ease: "easeOut" }}
className="flex flex-col items-center justify-center"
>
<h1 className="text-5xl font-bold mb-3">404</h1>
<p className="text-gray-500 mb-6">This page doesn't exist.</p>
<Link
href="/"
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition"
>
Back to Home
</Link>
</motion.div>
</main>
);
}
This gives your error page a smooth fade-in effect and makes the experience feel more polished.
Step 4: Add Metadata (SEO)
If you’re using the App Router, you can define metadata for your 404 page:
export const metadata = {
title: "Page Not Found | MyApp",
description: "The page you're looking for doesn't exist.",
};This ensures your 404 page has proper SEO meta tags and doesn’t harm your search rankings.
Step 5: Testing Your Custom 404 Page
Run your app locally:
npm run dev
Then open a non-existing route, for example:
http://localhost:3000/random-page
You should now see your custom design in action.
Best Practices
- Keep the design simple and clear — don’t overload the page.
- Include navigation links or a search bar to help users recover.
- Add branding to match your app’s design.
- Optionally, log 404 hits to analytics tools like Plausible or Google Analytics.
- Avoid making it a redirect — serve a true 404 HTTP status for SEO accuracy.
To read more about A Complete Beginner’s Guide to Getting Started with Next.js, refer to our blog A Complete Beginner’s Guide to Getting Started with Next.js