mirror of
https://github.com/hanzoai/next.js.git
synced 2026-10-30 14:46:38 +00:00
<!-- Thanks for opening a PR! Your contribution is much appreciated. To make sure your PR is handled as smoothly as possible we request that you follow the checklist sections below. Choose the right checklist for the change(s) that you're making: ## For Contributors ### Improving Documentation - Run `pnpm prettier-fix` to fix formatting issues before opening the PR. - Read the Docs Contribution Guide to ensure your contribution follows the docs guidelines: https://nextjs.org/docs/community/contribution-guide ### Fixing a bug - Related issues linked using `fixes #number` - Tests added. See: https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs - Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md ### Adding a feature - Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. (A discussion must be opened, see https://github.com/vercel/next.js/discussions/new?category=ideas) - Related issues/discussions are linked using `fixes #number` - e2e tests added (https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) - Documentation added - Telemetry added. In case of a feature if it's used or not. - Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md ## For Maintainers - Minimal description (aim for explaining to someone not on the team to understand the PR) - When linking to a Slack thread, you might want to share details of the conclusion - Link both the Linear (Fixes NEXT-xxx) and the GitHub issues - Add review comments if necessary to explain to the reviewer the logic behind a change ### What? This PR updates the documentation to clarify the interaction between `loading.js` and `layout.js`. ### Why? To address a common point of confusion and potential blocking navigation issues when layouts are dynamic. It provides clear guidance on how `loading.js` behaves relative to layouts and how to use `<Suspense>` for dynamic layout parts. ### How? - Added a "Good to know" callout in `loading.mdx` explaining that `loading.js` does not wrap the layout. - Introduced a new "Interaction with `loading.js`" section under Caveats in `layout.mdx`, including an explanation and a code example for wrapping dynamic layout components with `<Suspense>`. - Added a brief "Good to know" callout in `fetching-data.mdx` for consistency. Closes NEXT- Fixes # --> --- [Slack Thread](https://vercel.slack.com/archives/C05KYT5S9FF/p1772239839900129?thread_ts=1772239839.900129&cid=C05KYT5S9FF) <p><a href="https://cursor.com/agents/bc-d69fb736-2a00-5f68-9e28-9b31c194f12a"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a href="https://cursor.com/background-agent?bcId=bc-d69fb736-2a00-5f68-9e28-9b31c194f12a"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> </p> --------- Co-authored-by: Cursor Agent <[email protected]> Co-authored-by: Joseph Chamochumbi <[email protected]> Co-authored-by: Delba de Oliveira <[email protected]>
236 lines
7.5 KiB
Plaintext
236 lines
7.5 KiB
Plaintext
---
|
|
title: not-found.js
|
|
description: API reference for the not-found.js file.
|
|
---
|
|
|
|
Next.js provides two conventions to handle not found cases:
|
|
|
|
- **`not-found.js`**: Used when you call the [`notFound`](/docs/app/api-reference/functions/not-found) 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`](/docs/app/api-reference/functions/not-found) 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 (see [Status Codes](/docs/app/api-reference/file-conventions/loading#status-codes) for details about SEO).
|
|
|
|
```tsx filename="app/not-found.tsx" switcher
|
|
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>
|
|
)
|
|
}
|
|
```
|
|
|
|
```jsx filename="app/blog/not-found.js" switcher
|
|
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>
|
|
)
|
|
}
|
|
```
|
|
|
|
In the [component hierarchy](/docs/app/getting-started/project-structure#component-hierarchy), `not-found.js` renders between `loading.js` and `page.js`. It is wrapped by the `<Suspense>` boundary from `loading.js` and the error boundary from `error.js` in the same segment.
|
|
|
|
## `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.
|
|
|
|
The `global-not-found.js` file bypasses your app's normal rendering, which means you'll need to import any global styles, fonts, or other dependencies that your 404 page requires.
|
|
|
|
> **Good to know**: A smaller version of your global styles, and a simpler font family could improve performance of this page.
|
|
|
|
`global-not-found.js` 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`:
|
|
|
|
```tsx filename="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`:
|
|
|
|
```tsx filename="app/global-not-found.tsx" switcher
|
|
// Import global styles and fonts
|
|
import './globals.css'
|
|
import { Inter } from 'next/font/google'
|
|
import type { Metadata } from 'next'
|
|
|
|
const inter = Inter({ subsets: ['latin'] })
|
|
|
|
export const metadata: Metadata = {
|
|
title: '404 - Page Not Found',
|
|
description: 'The page you are looking for does not exist.',
|
|
}
|
|
|
|
export default function GlobalNotFound() {
|
|
return (
|
|
<html lang="en" className={inter.className}>
|
|
<body>
|
|
<h1>404 - Page Not Found</h1>
|
|
<p>This page does not exist.</p>
|
|
</body>
|
|
</html>
|
|
)
|
|
}
|
|
```
|
|
|
|
```jsx filename="app/global-not-found.js" switcher
|
|
// Import global styles and fonts
|
|
import './globals.css'
|
|
import { Inter } from 'next/font/google'
|
|
|
|
const inter = Inter({ subsets: ['latin'] })
|
|
|
|
export const metadata = {
|
|
title: '404 - Page Not Found',
|
|
description: 'The page you are looking for does not exist.',
|
|
}
|
|
|
|
export default function GlobalNotFound() {
|
|
return (
|
|
<html lang="en" className={inter.className}>
|
|
<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:
|
|
|
|
```tsx filename="app/not-found.tsx" switcher
|
|
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>
|
|
)
|
|
}
|
|
```
|
|
|
|
```jsx filename="app/not-found.jsx" switcher
|
|
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`](/docs/app/api-reference/functions/generate-metadata) function to customize the `<title>`, `<meta>`, and other head tags for your 404 page:
|
|
|
|
> **Good to know**: Next.js automatically injects `<meta name="robots" content="noindex" />` for pages that return a 404 status code, including `global-not-found.js` pages.
|
|
|
|
```tsx filename="app/global-not-found.tsx" switcher
|
|
import type { Metadata } from 'next'
|
|
|
|
export const metadata: Metadata = {
|
|
title: 'Not Found',
|
|
description: 'The page you are looking for does not exist.',
|
|
}
|
|
|
|
export default function GlobalNotFound() {
|
|
return (
|
|
<html lang="en">
|
|
<body>
|
|
<div>
|
|
<h1>Not Found</h1>
|
|
<p>The page you are looking for does not exist.</p>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
)
|
|
}
|
|
```
|
|
|
|
```jsx filename="app/global-not-found.js" switcher
|
|
export const metadata = {
|
|
title: 'Not Found',
|
|
description: 'The page you are looking for does not exist.',
|
|
}
|
|
|
|
export default function GlobalNotFound() {
|
|
return (
|
|
<html lang="en">
|
|
<body>
|
|
<div>
|
|
<h1>Not Found</h1>
|
|
<p>The page you are looking for does not exist.</p>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
)
|
|
}
|
|
```
|
|
|
|
## Version History
|
|
|
|
| Version | Changes |
|
|
| --------- | --------------------------------------------------- |
|
|
| `v15.4.0` | `global-not-found.js` introduced (experimental). |
|
|
| `v13.3.0` | Root `app/not-found` handles global unmatched URLs. |
|
|
| `v13.0.0` | `not-found` introduced. |
|