Skip to content

not-found.js

Next.js provides two conventions to handle not found cases:

  • not-found.js: Used when you call the notFound function in a route segment.
  • global-not-found.js: Used to define a global 404 page for unmatched routes across your entire app. This is handled at the routing level and doesn't depend on rendering a layout or page.

not-found.js

The not-found file is used to render UI when the notFound function is thrown within a route segment. Along with serving a custom UI, Next.js will return a 200 HTTP status code for streamed responses, and 404 for non-streamed responses.

app/not-found.tsx
import Link from 'next/link'
 
export default function NotFound() {
  return (
    <div>
      <h2>Not Found</h2>
      <p>Could not find requested resource</p>
      <Link href="/">Return Home</Link>
    </div>
  )
}

global-not-found.js (experimental)

The global-not-found.js file lets you define a 404 page for your entire application. Unlike not-found.js, which works at the route level, this is used when a requested URL doesn't match any route at all. Next.js skips rendering and directly returns this global page.

This is useful when you can't build a 404 page using a combination of layout.js and not-found.js. This can happen in two cases:

  • Your app has multiple root layouts (e.g. app/(admin)/layout.tsx and app/(shop)/layout.tsx), so there's no single layout to compose a global 404 from.
  • Your root layout is defined using top-level dynamic segments (e.g. app/[country]/layout.tsx), which makes composing a consistent 404 page harder.

To enable it, add the globalNotFound flag in next.config.ts:

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

Then, create a file in the root of the app directory: app/global-not-found.js:

app/global-not-found.tsx
export default function GlobalNotFound() {
  return (
    <html>
      <body>
        <h1>404 - Page Not Found</h1>
        <p>This page does not exist.</p>
      </body>
    </html>
  )
}

Unlike not-found.js, this file must return a full HTML document, including <html> and <body> tags.

Reference

Props

not-found.js or global-not-found.js components do not accept any props.

Good to know: In addition to catching expected notFound() errors, the root app/not-found.js and app/global-not-found.js files handle any unmatched URLs for your whole application. This means users that visit a URL that is not handled by your app will be shown the exported UI.

Examples

Data Fetching

By default, not-found is a Server Component. You can mark it as async to fetch and display data:

app/not-found.tsx
import Link from 'next/link'
import { headers } from 'next/headers'
 
export default async function NotFound() {
  const headersList = await headers()
  const domain = headersList.get('host')
  const data = await getSiteData(domain)
  return (
    <div>
      <h2>Not Found: {data.name}</h2>
      <p>Could not find requested resource</p>
      <p>
        View <Link href="/blog">all posts</Link>
      </p>
    </div>
  )
}

If you need to use Client Component hooks like usePathname to display content based on the path, you must fetch data on the client-side instead.

Metadata

For global-not-found.js, you can export a metadata object or a generateMetadata function to customize the <title>, <meta>, and other head tags for your 404 page:

app/global-not-found.tsx
export const metadata = {
  title: 'Not Found',
  description: 'The page you are looking for does not exist.',
}
 
export default function GlobalNotFound() {
  return (
    <div>
      <h1>Not Found</h1>
      <p>The page you are looking for does not exist.</p>
    </div>
  )
}

Version History

VersionChanges
v15.4.0global-not-found.js introduced (experimental).
v13.3.0Root app/not-found handles global unmatched URLs.
v13.0.0not-found introduced.