next
Version:
The React Framework
235 lines (189 loc) тАв 7.8 kB
Markdown
---
title: notFound
description: API Reference for the notFound function.
related:
links:
- app/api-reference/file-conventions/not-found
- app/api-reference/functions/forbidden
- app/api-reference/functions/unauthorized
---
The `notFound` function throws an error that renders a Next.js 404 page. It's useful for handling missing resources in your application. You can customize the UI using the [`not-found.js` file](/docs/app/api-reference/file-conventions/not-found).
Invoking `notFound()` throws a `NEXT_HTTP_ERROR_FALLBACK;404` error and terminates rendering of the route segment where it was thrown. Next.js also injects a `<meta name="robots" content="noindex" />` tag so the page is not indexed. Because it works by throwing, call it in the render path: a component, or a function a component `await`s. A call left in an un-awaited promise throws where nothing catches it, and no not-found UI renders (in development the server logs `тип unhandledRejection: NEXT_HTTP_ERROR_FALLBACK;404`).
`notFound()` can be invoked in [Server Components](/docs/app/getting-started/server-and-client-components), [Server Functions](/docs/app/getting-started/mutating-data), and [Route Handlers](/docs/app/api-reference/file-conventions/route).
```tsx filename="app/user/[id]/page.tsx" switcher
import { notFound } from 'next/navigation'
async function fetchUser(id: string) {
const res = await fetch('https://...')
if (!res.ok) return undefined
return res.json()
}
export default async function Profile({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
const user = await fetchUser(id)
if (!user) {
notFound()
}
// ...
}
```
```jsx filename="app/user/[id]/page.js" switcher
import { notFound } from 'next/navigation'
async function fetchUser(id) {
const res = await fetch('https://...')
if (!res.ok) return undefined
return res.json()
}
export default async function Profile({ params }) {
const { id } = await params
const user = await fetchUser(id)
if (!user) {
notFound()
}
// ...
}
```
## Good to know
You do not need to write `return notFound()`. Calling it is enough, because it throws an exception that stops function execution. TypeScript understands this from its [`never`](https://www.typescriptlang.org/docs/handbook/2/functions.html#never) return type, so a value you check first stays narrowed afterward:
```tsx
// fetchUser resolves to a user object, or undefined
const user = await fetchUser(id)
if (!user) {
notFound()
}
// user is defined here
return <Profile user={user} />
```
Like any exception, it travels up the call stack until something catches it. A `try/catch` around the call suppresses it, and the not-found UI won't render. If you need to catch errors near the call, use [`unstable_rethrow`](/docs/app/api-reference/functions/unstable_rethrow) to let the interrupt through first.
## Examples
### Calling `notFound()` after streaming has started
To keep a page's shell and loading UI visible while data loads, do the existence check inside a component wrapped in [`<Suspense>`](https://react.dev/reference/react/Suspense) instead of blocking the whole route. The idiomatic place for the check is the data-access function itself, awaited by the component that needs the data:
```tsx filename="app/blog/[slug]/page.tsx" switcher highlight={8}
import { Suspense } from 'react'
import Link from 'next/link'
import { notFound } from 'next/navigation'
async function getPost(slug: string) {
const res = await fetch(`https://api.example.com/posts/${slug}`)
if (res.status === 404) {
notFound()
}
if (!res.ok) {
throw new Error(`Failed to load post: ${res.status}`)
}
return res.json()
}
async function Article({ slug }: { slug: string }) {
const post = await getPost(slug)
return (
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
</article>
)
}
export default async function PostPage({ params }: PageProps<'/blog/[slug]'>) {
const { slug } = await params
return (
<section>
<Link href="/blog">Blog</Link>
<Suspense fallback={<p>Loading...</p>}>
<Article slug={slug} />
</Suspense>
</section>
)
}
```
```jsx filename="app/blog/[slug]/page.js" switcher highlight={8}
import { Suspense } from 'react'
import Link from 'next/link'
import { notFound } from 'next/navigation'
async function getPost(slug) {
const res = await fetch(`https://api.example.com/posts/${slug}`)
if (res.status === 404) {
notFound()
}
if (!res.ok) {
throw new Error(`Failed to load post: ${res.status}`)
}
return res.json()
}
async function Article({ slug }) {
const post = await getPost(slug)
return (
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
</article>
)
}
export default async function PostPage({ params }) {
const { slug } = await params
return (
<section>
<Link href="/blog">Blog</Link>
<Suspense fallback={<p>Loading...</p>}>
<Article slug={slug} />
</Suspense>
</section>
)
}
```
When the post doesn't exist, `getPost` calls `notFound()`, which throws. Because this happens during rendering, the exception propagates to the nearest [`not-found`](/docs/app/api-reference/file-conventions/not-found) boundary, which renders in place of the streamed-in content, even though the page shell has already been sent.
Add a `not-found.tsx` alongside the route to define that UI. Without one, the nearest parent `not-found` boundary renders, falling back to Next.js's default 404 page:
```tsx filename="app/blog/[slug]/not-found.tsx" switcher
export default function NotFound() {
return (
<section>
<h1>Post not found</h1>
<p>The post you're looking for doesn't exist.</p>
</section>
)
}
```
```jsx filename="app/blog/[slug]/not-found.js" switcher
export default function NotFound() {
return (
<section>
<h1>Post not found</h1>
<p>The post you're looking for doesn't exist.</p>
</section>
)
}
```
The trade-off is the HTTP status code. Because the check runs inside the `<Suspense>` boundary, the response has already begun streaming as a `200`, and the status can't change once streaming has started. The `noindex` tag keeps a soft 404 out of search results. To return a real `404` status, the resource has to be checked before the response streams. With [Cache Components](/docs/app/getting-started/caching), every dynamic route streams a static shell first, so run that check in [`proxy`](/docs/app/api-reference/file-conventions/proxy) instead. See [Status codes](/docs/app/api-reference/file-conventions/loading#status-codes).
### Serving a 404 from a Route Handler
`notFound()` also works in a [Route Handler](/docs/app/api-reference/file-conventions/route), where it serves a `404` to the caller.
```tsx filename="app/api/posts/[slug]/route.ts" switcher
import { NextResponse } from 'next/server'
import { notFound } from 'next/navigation'
export async function GET(
request: Request,
{ params }: RouteContext<'/api/posts/[slug]'>
) {
const { slug } = await params
const res = await fetch(`https://api.example.com/posts/${slug}`)
if (!res.ok) {
notFound()
}
return NextResponse.json(await res.json())
}
```
```jsx filename="app/api/posts/[slug]/route.js" switcher
import { NextResponse } from 'next/server'
import { notFound } from 'next/navigation'
export async function GET(request, { params }) {
const { slug } = await params
const res = await fetch(`https://api.example.com/posts/${slug}`)
if (!res.ok) {
notFound()
}
return NextResponse.json(await res.json())
}
```
## Version History
| Version | Changes |
| --------- | ---------------------- |
| `v13.0.0` | `notFound` introduced. |