UNPKG

next

Version:

The React Framework

195 lines (141 loc) • 9.81 kB
--- 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 Streaming page responses begin with a `200` status code. The initial response headers set the status for the rest of the response. Next.js can then stream redirects and HTTP access fallbacks with [`redirect`](/docs/app/api-reference/functions/redirect), [`notFound`](/docs/app/api-reference/functions/not-found), [`forbidden`](/docs/app/api-reference/functions/forbidden), or [`unauthorized`](/docs/app/api-reference/functions/unauthorized). 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”. The `noindex` tag keeps them out of search results. Run the corresponding check before the response body streams to return a `404`, `403`, or `401` status for compliance or analytics. For a `404`, you can check the slug in [Proxy](/docs/app/api-reference/file-conventions/proxy), then rewrite it to a not-found route or [return 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. Calling `notFound()`, `forbidden()`, or `unauthorized()` before this point preserves its `404`, `403`, or `401` status. Starting the stream sends the response headers and fixes the status code. </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). See the [Streaming guide](/docs/app/guides/streaming) for more on how streaming works, including granular Suspense patterns, Route Handler streaming, and infrastructure considerations. `<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. |