Some checks failed
Test examples / Test Examples (20) (push) Has been cancelled
Test examples / Test Examples (22) (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Trigger Release / start (push) Has been cancelled
Stale issue handler / stale (push) Has been cancelled
Update Font Data / create-pull-request (push) Has been cancelled
build-and-deploy / deploy-target (push) Has been cancelled
build-and-deploy / build (push) Has been cancelled
build-and-deploy / stable - aarch64-unknown-linux-musl - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-unknown-linux-musl - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-unknown-linux-gnu - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-unknown-linux-gnu - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-pc-windows-msvc - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-pc-windows-msvc - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-apple-darwin - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-apple-darwin - node@16 (push) Has been cancelled
build-and-deploy / build-wasm (nodejs) (push) Has been cancelled
build-and-deploy / build-wasm (web) (push) Has been cancelled
build-and-deploy / Deploy preview tarball (push) Has been cancelled
build-and-deploy / Potentially publish release (push) Has been cancelled
build-and-deploy / publish-turbopack-npm-packages (push) Has been cancelled
build-and-deploy / Deploy examples (push) Has been cancelled
build-and-deploy / thank you, build (push) Has been cancelled
build-and-deploy / Upload Turbopack Bytesize metrics to Datadog (push) Has been cancelled
Rspack Next.js development integration tests / Rspack integration tests (push) Has been cancelled
Rspack Next.js production integration tests / Rspack integration tests (push) Has been cancelled
Turbopack Next.js development integration tests / Next.js integration tests (push) Has been cancelled
Turbopack Next.js production integration tests / Next.js integration tests (push) Has been cancelled
Update Rspack test manifest / Update and upload Rspack development test manifest (push) Has been cancelled
Update Rspack test manifest / Update and upload Rspack production test manifest (push) Has been cancelled
Upload bundler test manifests to areweturboyet.com / Upload test results (push) Has been cancelled
Update React / create-pull-request (push) Has been cancelled
test-e2e-project-reset-cron / reset-test-project (push) Has been cancelled
Notify about the top 15 issues/PRs/feature requests (most reacted) in the last 90 days / run (push) Has been cancelled
195 lines
9.5 KiB
Plaintext
195 lines
9.5 KiB
Plaintext
---
|
||
title: loading.js
|
||
description: API reference for the loading.js file.
|
||
---
|
||
|
||
The special file `loading.js` helps you create meaningful Loading UI with [React Suspense](https://react.dev/reference/react/Suspense). With this convention, you can show an [instant loading state](#instant-loading-states) from the server while the content of a route segment streams in. The new content is automatically swapped in once complete.
|
||
|
||
<Image
|
||
alt="Loading UI"
|
||
srcLight="/docs/light/loading-ui.png"
|
||
srcDark="/docs/dark/loading-ui.png"
|
||
width="1600"
|
||
height="691"
|
||
/>
|
||
|
||
```tsx filename="app/feed/loading.tsx" switcher
|
||
export default function Loading() {
|
||
// Or a custom loading skeleton component
|
||
return <p>Loading...</p>
|
||
}
|
||
```
|
||
|
||
```jsx filename="app/feed/loading.js" switcher
|
||
export default function Loading() {
|
||
// Or a custom loading skeleton component
|
||
return <p>Loading...</p>
|
||
}
|
||
```
|
||
|
||
Inside the `loading.js` file, you can add any light-weight loading UI. You may find it helpful to use the [React Developer Tools](https://react.dev/learn/react-developer-tools) to manually toggle Suspense boundaries.
|
||
|
||
By default, this file is a [Server Component](/docs/app/getting-started/server-and-client-components) - but can also be used as a Client Component through the `"use client"` directive.
|
||
|
||
## Reference
|
||
|
||
### Parameters
|
||
|
||
Loading UI components do not accept any parameters.
|
||
|
||
## Behavior
|
||
|
||
### Navigation
|
||
|
||
- The Fallback UI is [prefetched](/docs/app/getting-started/linking-and-navigating#prefetching), making navigation immediate unless prefetching hasn't completed.
|
||
- Navigation is interruptible, meaning changing routes does not need to wait for the content of the route to fully load before navigating to another route.
|
||
- Shared layouts remain interactive while new route segments load.
|
||
|
||
### Instant Loading States
|
||
|
||
An instant loading state is fallback UI that is shown immediately upon navigation. You can prerender loading indicators such as skeletons and spinners, or a small but meaningful part of future screens such as a cover photo, title, etc. This helps users understand the app is responding and provides a better user experience.
|
||
|
||
Create a loading state by adding a `loading.js` file inside a folder.
|
||
|
||
<Image
|
||
alt="loading.js special file"
|
||
srcLight="/docs/light/loading-special-file.png"
|
||
srcDark="/docs/dark/loading-special-file.png"
|
||
width="1600"
|
||
height="606"
|
||
/>
|
||
|
||
```tsx filename="app/dashboard/loading.tsx" switcher
|
||
export default function Loading() {
|
||
// You can add any UI inside Loading, including a Skeleton.
|
||
return <LoadingSkeleton />
|
||
}
|
||
```
|
||
|
||
```jsx filename="app/dashboard/loading.js" switcher
|
||
export default function Loading() {
|
||
// You can add any UI inside Loading, including a Skeleton.
|
||
return <LoadingSkeleton />
|
||
}
|
||
```
|
||
|
||
In the same folder, `loading.js` will be nested inside `layout.js`. It will automatically wrap the `page.js` file and any children below in a `<Suspense>` boundary.
|
||
|
||
<Image
|
||
alt="loading.js overview"
|
||
srcLight="/docs/light/loading-overview.png"
|
||
srcDark="/docs/dark/loading-overview.png"
|
||
width="1600"
|
||
height="768"
|
||
/>
|
||
|
||
In the [component hierarchy](/docs/app/getting-started/project-structure#component-hierarchy), `loading.js` wraps `not-found.js`, `page.js`, and nested `layout.js` files in a `<Suspense>` boundary. It does **not** wrap the `layout.js`, `template.js`, or `error.js` in the same segment.
|
||
|
||
> **Good to know**: If the layout accesses uncached or runtime data (e.g. `cookies()`, `headers()`, or uncached fetches), `loading.js` will not show a fallback for it.
|
||
>
|
||
> - **Without [Cache Components](/docs/app/getting-started/caching):** Navigation blocks until the layout finishes rendering.
|
||
> - **With [Cache Components](/docs/app/getting-started/caching):** Uncached or runtime data access in the layout must be explicitly wrapped in `<Suspense>`, otherwise Next.js guides you with a build-time error. The static shell streams first, and the uncached content fills in.
|
||
>
|
||
> To ensure instant navigation, move uncached data fetching from `layout.js` into `page.js`, or wrap the runtime data access in your layout in its own `<Suspense>` boundary. See [layout.js Caveats](/docs/app/api-reference/file-conventions/layout#interaction-with-loadingjs) for details and examples.
|
||
|
||
### SEO
|
||
|
||
- For bots that only scrape static HTML, and cannot execute JavaScript like a full browser, such as Twitterbot, Next.js resolves [`generateMetadata`](/docs/app/api-reference/functions/generate-metadata) before streaming UI, and metadata is placed in the `<head>` of the initial HTML.
|
||
- Otherwise, [streaming metadata](/docs/app/api-reference/functions/generate-metadata#streaming-metadata) may be used. Next.js automatically detects user agents to choose between blocking and streaming behavior.
|
||
- Since streaming is server-rendered, it does not impact SEO. You can use the [Rich Results Test](https://search.google.com/test/rich-results) tool from Google to see how your page appears to Google's web crawlers and view the serialized HTML ([source](https://web.dev/rendering-on-the-web/#seo-considerations)).
|
||
|
||
### Status Codes
|
||
|
||
When streaming, a `200` status code will be returned to signal that the request was successful.
|
||
|
||
The server can still communicate errors or issues to the client within the streamed content itself, for example, when using [`redirect`](/docs/app/api-reference/functions/redirect) or [`notFound`](/docs/app/api-reference/functions/not-found). Because the response headers have already been sent to the client, the status code of the response cannot be updated.
|
||
|
||
For example, when a 404 page is streamed to the client, Next.js includes a `<meta name="robots" content="noindex">` tag in the streamed HTML. This prevents search engines from indexing that URL even if the HTTP status is 200. See Google’s guidance on the [`robots` meta tag](https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag).
|
||
|
||
Some crawlers may label these responses as “soft 404s”. In the streaming case, this does not lead to indexation because the page is explicitly marked `noindex` in the HTML.
|
||
|
||
If you need a 404 status, for compliance or analytics, ensure the resource exists before the response body is streamed, so that the server can set the HTTP status code.
|
||
|
||
You can run this check in [`proxy`](/docs/app/api-reference/file-conventions/proxy) to rewrite missing slugs to a not-found route, or [produce a 404 response](/docs/app/api-reference/file-conventions/proxy#producing-a-response). Keep proxy checks fast, and avoid fetching full content there.
|
||
|
||
<details>
|
||
<summary>When is the response body streamed?</summary>
|
||
|
||
The response body starts streaming when a Suspense fallback renders (for example, a `loading.tsx`) or when a Server Component suspends under a `Suspense` boundary. Place `notFound()` before those boundaries and before any `await` that may suspend.
|
||
|
||
To start streaming, the response headers must be set. This is why it is not possible to change the status code after streaming started.
|
||
|
||
</details>
|
||
|
||
### Browser limits
|
||
|
||
[Some browsers](https://bugs.webkit.org/show_bug.cgi?id=252413) buffer a streaming response. You may not see the streamed response until the response exceeds 1024 bytes. This typically only affects “hello world” applications, but not real applications.
|
||
|
||
## Platform Support
|
||
|
||
| Deployment Option | Supported |
|
||
| ------------------------------------------------------------------- | ----------------- |
|
||
| [Node.js server](/docs/app/getting-started/deploying#nodejs-server) | Yes |
|
||
| [Docker container](/docs/app/getting-started/deploying#docker) | Yes |
|
||
| [Static export](/docs/app/getting-started/deploying#static-export) | No |
|
||
| [Adapters](/docs/app/getting-started/deploying#adapters) | Platform-specific |
|
||
|
||
Learn how to [configure streaming](/docs/app/guides/self-hosting#streaming-and-suspense) when self-hosting Next.js.
|
||
|
||
## Examples
|
||
|
||
### Streaming with Suspense
|
||
|
||
In addition to `loading.js`, you can also manually create Suspense Boundaries for your own UI components. The App Router supports streaming with [Suspense](https://react.dev/reference/react/Suspense).
|
||
|
||
`<Suspense>` works by wrapping a component that performs an asynchronous action (e.g. fetch data), showing fallback UI (e.g. skeleton, spinner) while it's happening, and then swapping in your component once the action completes.
|
||
|
||
```tsx filename="app/dashboard/page.tsx" switcher
|
||
import { Suspense } from 'react'
|
||
import { PostFeed, Weather } from './Components'
|
||
|
||
export default function Posts() {
|
||
return (
|
||
<section>
|
||
<Suspense fallback={<p>Loading feed...</p>}>
|
||
<PostFeed />
|
||
</Suspense>
|
||
<Suspense fallback={<p>Loading weather...</p>}>
|
||
<Weather />
|
||
</Suspense>
|
||
</section>
|
||
)
|
||
}
|
||
```
|
||
|
||
```jsx filename="app/dashboard/page.js" switcher
|
||
import { Suspense } from 'react'
|
||
import { PostFeed, Weather } from './Components'
|
||
|
||
export default function Posts() {
|
||
return (
|
||
<section>
|
||
<Suspense fallback={<p>Loading feed...</p>}>
|
||
<PostFeed />
|
||
</Suspense>
|
||
<Suspense fallback={<p>Loading weather...</p>}>
|
||
<Weather />
|
||
</Suspense>
|
||
</section>
|
||
)
|
||
}
|
||
```
|
||
|
||
By using Suspense, you get the benefits of:
|
||
|
||
1. **Streaming Server Rendering** - Progressively rendering HTML from the server to the client.
|
||
2. **Selective Hydration** - React prioritizes what components to make interactive first based on user interaction.
|
||
|
||
For more Suspense examples and use cases, please see the [React Documentation](https://react.dev/reference/react/Suspense).
|
||
|
||
## Version History
|
||
|
||
| Version | Changes |
|
||
| --------- | --------------------- |
|
||
| `v13.0.0` | `loading` introduced. |
|