Rendering & DataExtra· 40 min read

Error and Not-Found Pages

When something breaks or a page does not exist, special error.js and not-found.js files show a friendly message instead of a crash.

What you will learn

  • Catch render errors with an error.js boundary
  • Show a custom 404 with not-found.js and notFound()
  • Let the user retry with the reset function

Two things that go wrong in real apps

Two problems happen on every real site: something throws an error (a database is down, an API fails), and someone visits a page that does not exist (a deleted post, a typo in the URL). If you do nothing, the user sees an ugly crash screen. Next.js gives you one special file for each case.

Catching errors with error.js

Put a file named error.js in a route folder, and it becomes an error boundary — a safety net that catches any error thrown while rendering that route and shows your friendly message instead of crashing the whole app. (An error boundary is a component that "catches" errors from the components inside it, like a try/catch for your UI.)

An error.js file must be a Client Component (it needs interactivity for the retry button), so it starts with use client. Next.js hands it two props: the error that happened and a reset function that retries.

app/dashboard/error.js — catches errors in the dashboard route
'use client';

export default function Error({ error, reset }) {
  return (
    <div>
      <h2>Something went wrong.</h2>
      <p>{error.message}</p>
      <button onClick={() => reset()}>Try again</button>
    </div>
  );
}

Note: Output (when the dashboard page throws an error): Something went wrong. Failed to load sales data [ Try again ] Instead of a crash, the user sees a calm message and a button. Clicking "Try again" calls reset(), which re-renders the route — handy if the error was temporary.

Showing a custom 404 with not-found.js

A 404 means "this page does not exist". Put a not-found.js file in a route folder to design your own 404 screen for that area, instead of the plain default.

A custom not-found page for the blog section
// app/blog/not-found.js
import Link from 'next/link';

export default function NotFound() {
  return (
    <div>
      <h2>Post not found</h2>
      <p>We could not find that blog post.</p>
      <Link href="/blog">Back to all posts</Link>
    </div>
  );
}

Note: Output (when a blog post is missing): Post not found We could not find that blog post. Back to all posts <- a link home Much friendlier than a blank "404". The user gets a clear message and a way back.

Triggering the 404 yourself with notFound()

Often you decide a page is missing — for example, someone visits /blog/9999 but post 9999 does not exist. Call the notFound() helper and Next.js stops rendering the page and shows your not-found.js instead.

Call notFound() when the requested item does not exist
// app/blog/[id]/page.js
import { notFound } from 'next/navigation';

export default async function PostPage({ params }) {
  const post = await getPost(params.id);
  if (!post) {
    notFound();   // stops here and shows not-found.js
  }
  return <h1>{post.title}</h1>;
}

Note: Output (visiting /blog/9999 when it does not exist): The page rendering stops at notFound(), and Next.js renders app/blog/not-found.js instead — your custom "Post not found" screen, with a proper 404 status code for search engines.

What happens when notFound() runs, step by step

Here is exactly what Next.js does when a visitor opens a missing post like /blog/9999:

  1. The visitor opens /blog/9999. Next.js runs the PostPage Server Component on the server.
  2. The component calls getPost('9999'), which looks for that post and finds nothing — post is empty.
  3. The if (!post) check is true, so the code calls notFound().
  4. notFound() stops rendering the page right there — the <h1> below it never runs.
  5. Next.js looks up the nearest not-found.js (here app/blog/not-found.js) and renders it instead.
  6. The visitor sees your friendly "Post not found" screen, and the response carries a real 404 status code so search engines treat it correctly.

Which file for which problem

FileHandlesHow it triggers
error.jsA thrown error during renderAutomatically, when code throws
not-found.jsA missing page (404)A bad URL, or you call notFound()
loading.jsA page that is still loadingAutomatically, while fetching

Tip: These files are scoped like layouts. An error.js in app/dashboard/ only catches errors inside the dashboard, so the rest of your site keeps working. Put a top-level one in app/ as a catch-all safety net.

Watch out: Returning a 404 with the correct status code matters for SEO — search engines should not index a "not found" page as real content. notFound() sets that status for you, which is why it beats just rendering a "missing" message inside the normal page.

Q. A visitor requests a blog post that does not exist. What is the clean Next.js way to show a 404?

Answer: Calling notFound() stops rendering and shows your not-found.js with the correct 404 status code, which is both user-friendly and SEO-correct.

✍️ Practice

  1. Add an error.js to a route, force an error (throw in the page), and confirm the friendly screen with a "Try again" button appears.
  2. Add a not-found.js to /blog and call notFound() for a missing post id.

🏠 Homework

  1. Give your blog both an error.js (with a working retry button) and a not-found.js, then trigger each one to confirm they work.
Want to learn this with a mentor?

CodingClave runs guided, project-based training (28-day, 45-day & 6-month batches).

Explore Training →