* [Beta] Initial API doc for Suspense
This doesn't include everything but I think it's good enough for
a start.
I didn't add any interactive examples yet because I want to use the
`use` API, so I'll wait until that's documented.
* edits
Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
### Displaying a fallback while something is loading {/*displaying-a-fallback-while-something-is-loading*/}
You can wrap any part of your application with a Suspense component. If either data or code in <CodeStepstep={2}>its children</CodeStep> hasn't loaded yet, React will switch to rendering the <CodeStepstep={1}>`fallback`</CodeStep> prop instead. For example:
Suppose that `Comments` takes longer to load than `Post`. Without a Suspense boundary, React wouldn't be able to show either component until both have loaded — `Post` would be blocked by `Comments`.
Because of the Suspense boundary, `Post` doesn't need to wait for `Comments`. React renders `LoadingSpinner` in its place. Once `Comments` finishes loading, React replaces `LoadingSpinner` with `Comments`.
Suspense will never show unintentional "holes" in your content. For example, if `PhotoAlbums` has loaded but `Notes` have not, with the structure below, it will still show a `LoadingIndicator` instead of the entire `Grid`:
```js {4-7}
<>
<ProfileHeader/>
<Suspensefallback={<LoadingSpinner/>}>
<Grid>
<PhotoAlbums/>
<Notes/>
</Grid>
</Suspense>
</>
```
To reveal nested content as it loads, you need to [add more Suspense boundaries.](#revealing-nested-content-as-it-loads)
<Gotcha>
**Only Suspense-enabled data sources will activate a Suspense boundary.** These data sources are said to *suspend* when the data needed to render has not yet loaded. Currently, Suspense is only supported for:
- Data fetching with opinionated frameworks like [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/), [Next.js](https://nextjs.org/docs/advanced-features/react-18), [Hydrogen](https://hydrogen.shopify.dev/), and [Remix](https://remix.run/)
Suspense-enabled data fetching without the use of an opinionated framework is not yet supported. The requirements for implementing a Suspense-enabled data source are unstable and undocumented. An official API for integrating data sources with Suspense will be released in a future version of React.
Suspense does not detect when data is fetched inside an Effect or event handler.
</Gotcha>
---
### Revealing nested content as it loads {/*revealing-nested-content-as-it-loads*/}
When a component suspends, it activates the fallback of only the nearest parent Suspense boundary. This means you can nest multiple Suspense boundaries to create a loading sequence. Each Suspense boundary's fallback will be filled in as the next level of content becomes available.
To illustrate, consider the following example:
```js {1,4}
<Suspensefallback={<BigSpinner/>}>
<MainContent>
<Post/>
<Suspensefallback={<CommentsGlimmer/>}>
<Comments/>
</Suspense>
</MainContent>
</Suspense>
```
The sequence will be:
- If `Post` hasn't loaded yet, `BigSpinner` is shown in place of the entire main content area.
- Once `Post` finishes loading, `BigSpinner` is replaced by the main content.
- If `Comments` hasn't loaded yet, `CommentsGlimmer` is shown in its place.
- Finally, once `Comments` finishes loading, it replaces `CommentsGlimmer`.
---
### Lazy loading components with Suspense {/*suspense-for-code-splitting*/}
The [`lazy`](/apis/react/lazy) API is powered by Suspense. When you render a component imported with `lazy`, it will suspend if it hasn't loaded yet. This allows you to display a loading indicator while your component's code is loading.
In this example, the code for `MarkdownPreview` won't be loaded until you attempt to render it. If `MarkdownPreview` hasn't loaded yet, `Loading` will be shown in its place. Try ticking the checkbox:
This demo loads with an artificial delay. The next time you untick and tick the checkbox, `Preview` will be cached, so there will be no loading state displayed. To see the loading state again, click "Reset" on the sandbox.
---
## Reference {/*reference*/}
### `Suspense` {/*suspense*/}
#### Props {/*suspense-props*/}
* `children`: The actual UI you intend to render. If `children` suspends while rendering, the Suspense boundary will switch to rendering `fallback`.
* `fallback`: An alternate UI to render in place of the actual UI if it has not finished loading. Any valid React node is accepted, though in practice, a fallback is a lightweight placeholder view, such as a loading spinner or skeleton. Suspense will automatically switch to `fallback` when `children` suspends, and back to `children` when the data is ready. If `fallback` suspends while rendering, it will activate the closest parent Suspense boundary.
### Caveats {/*caveats*/}
- React does not preserve any state for renders that got suspended before they were able to mount for the first time. When the component has loaded, React will retry rendering the suspended tree from scratch.
- If Suspense was displaying content for the tree, but then it suspended again, the `fallback` will be shown again unless the update causing it was caused by [`startTransition`](/apis/react/startTransition) or [`useDeferredValue`](/apis/react/useDeferredValue).
- If React needs to hide the already visible content because it suspended again, it will clean up [layout Effects](/apis/react/useLayoutEffect) in the content tree. When the content is ready to be shown again, React will fire the layout Effects again. This lets you make sure that Effects measuring the DOM layout don't try to do this while the content is hidden.
- React includes under-the-hood optimizations like *Streaming Server Rendering* and *Selective Hydration* that are integrated with Suspense. Read [an architectural overview](https://github.com/reactwg/react-18/discussions/37) and watch [a technical talk](https://www.youtube.com/watch?v=pj5N-Khihgc) to learn more.
---
## Troubleshooting {/*troubleshooting*/}
### How do I prevent the UI from being replaced by a fallback during an update? {/*preventing-unwanted-fallbacks*/}
Replacing visible UI with a fallback creates a jarring user experience. This can happen when an update causes a component to suspend, and the nearest Suspense boundary is already showing content to the user.
To prevent this from happening, mark the update as non-urgent using [`startTransition`](/apis/react/startTransition). During a transition, React will wait until enough data has loaded to prevent an unwanted fallback from appearing:
```js {2-3,5}
function handleNextPageClick() {
// If this update suspends, don't hide the already displayed content
startTransition(() => {
setCurrentPage(currentPage + 1);
});
}
```
This will avoid hiding existing content. However, any newly rendered `Suspense` boundaries will still immediately display fallbacks to avoid blocking the UI and let the user see the content as it becomes available.
**React will only prevent unwanted fallbacks during non-urgent updates**. It will not delay a render if it's the result of an urgent update. You must opt in with an API like [`startTransition`](/apis/react/startTransition) or [`useDeferredValue`](/apis/react/useDeferredValue).
If your router is integrated with Suspense, it should wrap its updates into [`startTransition`](/apis/react/startTransition) automatically.