Course outline · 0% complete

0/29 lessons0%

Course overview →

notFound and catch-all routes

lesson 5-3 · ~8 min · 17/29

Saying 404 on purpose

What if /blog/does-not-exist is requested and your fetch finds no post? Rendering an empty page is wrong, and throwing shows the error boundary. The honest answer is a 404, and Next.js gives you a function for it:

import { notFound } from "next/navigation";

export default async function PostPage({ params }) {
  const { slug } = await params;
  const post = await getPost(slug);
  if (!post) notFound();
  return <h1>{post.title}</h1>;
}

Calling notFound() stops rendering and shows the nearest not-found.js file, which you write like any other special file:

// app/blog/not-found.js
export default function NotFound() {
  return <p>That post does not exist.</p>;
}

The response also carries a real HTTP 404 status, which search engines respect. You know why that matters from Backend with Node.js: status codes are the contract of the web.

Quiz

What happens when a server component calls notFound()?

Catch-all segments

One more bracket trick. [slug] matches exactly one segment. Add three dots and it matches any depth:

app/docs/[...parts]/page.js
  • /docs/introparts === ["intro"]
  • /docs/api/auth/tokensparts === ["api", "auth", "tokens"]

The param becomes an array of segments. Catch-alls power docs sites and wikis where the hierarchy is data, not folders. Use them sparingly. If the depth is fixed, prefer explicit folders.

Problem

With app/docs/[...parts]/page.js, a user visits /docs/guides/routing. How many strings are in the parts array?