`not-found.js` vs `global-not-found.js` in Next.js

not-found.js renders when a route segment calls notFound(). global-not-found.js catches URLs that never matched a route at all. Learn when each one runs.

7 min read

Next.js not-found.js renders custom UI when a route segment calls its notFound function, such as a blog post lookup that finds no matching record. The newer global-not-found.js handles a different case: a URL that never matched any route in the app in the first place.

App.tsxApp.tsx
// app/blog/[slug]/not-found.tsx
export default function NotFound() {
  return <p>Post not found.</p>;
}

Placed next to a dynamic blog route, this file renders whenever that route calls its not-found function, such as after a failed lookup for the requested post.

Triggering not-found.js

The function that triggers it, imported from next/navigation, throws an error that Next.js catches and routes to the nearest matching file up the tree.

App.tsxApp.tsx
// app/blog/[slug]/page.tsx
import { notFound } from "next/navigation";
 
type Params = Promise<{ slug: string }>;
 
export default async function Page({ params }: { params: Params }) {
  const { slug } = await params;
  const post = await getPost(slug);
  if (!post) notFound();
  return <article>{post.title}</article>;
}

A root file placed directly in the app directory also automatically catches any URL that does not match a route at all, in addition to explicit calls inside a page. This is why most apps never need the global variant.

What the global variant adds

The global variant is an experimental convention for apps where a single layout cannot compose a consistent 404 page, most commonly when the project defines multiple root layouts or a root layout with a dynamic segment. It skips the app's normal rendering entirely, so it needs a full HTML document of its own.

App.tsxApp.tsx
// app/global-not-found.tsx
import "./globals.css";
 
export default function GlobalNotFound() {
  return (
    <html lang="en">
      <body>404 - Page Not Found</body>
    </html>
  );
}

Enabling this file requires a config flag first, since it has no effect until that flag is set. This is easy to miss during setup: the file can sit in the app directory correctly named and still be completely ignored until the corresponding flag is turned on in the config.

typescripttypescript
// next.config.ts
import type { NextConfig } from "next";
 
const nextConfig: NextConfig = {
  experimental: { globalNotFound: true },
};
 
export default nextConfig;

Setting this flag and creating the file together is what activates the global fallback; adding only one of the two has no visible effect at all.

Choosing between them

SituationFile to use
A specific lookup fails inside a routeThe segment-level file
No single shared layout exists to compose a 404 fromThe global variant
Every other caseA root file is enough

Common mistake

Forgetting to import global styles and fonts inside the global variant is a common mistake, since this file bypasses the layout that normally provides them. It also only sees the operating system color scheme, so an app with an explicit light or dark toggle needs to apply that theme directly inside this file.

For a related convention that also needs its own imports because it bypasses normal rendering, see colocation in the App Router, which explains what does and does not get pulled into a route automatically. The regular not-found function pairs closely with dynamic segments, covered in dynamic routes in Next.js.

Rune AI

Rune AI

Key Insights

  • not-found.js renders when the notFound function is called inside a route segment.
  • global-not-found.js catches URLs that never matched a route in the app.
  • global-not-found.js is experimental and needs the globalNotFound config flag enabled.
  • global-not-found.js bypasses layouts entirely and must include its own html and body tags.
  • Most apps only need a root not-found.js, not the global variant.
RunePowered by Rune AI

Frequently Asked Questions

Do I need both files in the same project?

No. Most apps only need not-found.js. global-not-found.js is for the specific case where a single layout cannot compose a consistent 404 page, such as an app with multiple root layouts.

Does global-not-found.js need to be enabled with a config flag?

Yes. It is experimental and requires setting the globalNotFound flag inside the experimental object in next.config.ts before Next.js will use the file.

Why does global-not-found.js need its own html and body tags?

Because it bypasses the app's normal layout rendering entirely, it has no parent layout supplying those tags, so it must define a full HTML document itself.

Conclusion

not-found.js handles a resource that a route segment could not find at render time, while global-not-found.js catches a URL that never matched any route in the app at all. Reach for the global file only when a single layout genuinely cannot express a consistent 404 page.