Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
0f92fef
[Suspense] Add 'What activates a Suspense boundary' section
aurorascharff Jun 27, 2026
4ddc1e3
[Suspense] Clarify image activation wording (review feedback)
aurorascharff Jun 27, 2026
021f94d
Define "suspense-enabled-framework"
aurorascharff Jun 27, 2026
14e8f67
Update outdated framework-less data fetching note to reference use()
aurorascharff Jun 27, 2026
d34d3ce
fix: clarify Suspense boundary activation and fallback behavior
aurorascharff Jun 30, 2026
2e01ae7
Add code example
aurorascharff Jun 30, 2026
f6d28e6
update styling
aurorascharff Jun 30, 2026
16fa091
Cleanup caveat
aurorascharff Jun 30, 2026
54958a7
add link to blogpost
aurorascharff Jun 30, 2026
6f6185d
Replace notes with link to suspense docs
aurorascharff Jul 1, 2026
b7c2094
Tweak caveat
aurorascharff Jul 1, 2026
201b5e3
Update caveat to clarify streaming behavior during server rendering
aurorascharff Jul 1, 2026
d2df82a
Address feedback
aurorascharff Jul 2, 2026
b33c637
address feedback
aurorascharff Jul 2, 2026
9091bee
Clarify documentation on Suspense-enabled frameworks
aurorascharff Jul 2, 2026
9be6345
Update links and wording
aurorascharff Jul 2, 2026
025ee7a
Improve example
aurorascharff Jul 3, 2026
2dfe10f
Clarify heading for Suspense-enabled frameworks section
aurorascharff Jul 3, 2026
567cdd4
Restructure page
aurorascharff Jul 5, 2026
c45c9b0
Update to be docs compliant and clarify images
aurorascharff Jul 6, 2026
1ed5be2
Address review feedback
aurorascharff Jul 8, 2026
3eb0f38
Add more suspense examples
aurorascharff Jul 8, 2026
dd5449e
Merge branch 'suspense-what-activates' of github.com:aurorascharff/re…
aurorascharff Jul 8, 2026
d63cade
Add more examples
aurorascharff Jul 8, 2026
d56099a
Clarify image loading behavior during <ViewTransition> updates in Sus…
aurorascharff Jul 8, 2026
2942718
Enhance Suspense documentation with additional details on font and im…
aurorascharff Jul 8, 2026
551cdf6
Update
aurorascharff Jul 9, 2026
0d41392
Enhance Suspense documentation with examples of stylesheet loading be…
aurorascharff Jul 9, 2026
5cc4a30
agent review
aurorascharff Jul 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 1 addition & 11 deletions src/content/reference/react-dom/server/renderToPipeableStream.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,17 +284,7 @@ Streaming does not need to wait for React itself to load in the browser, or for

<Note>

**Only Suspense-enabled data sources will activate the Suspense component.** They include:

- Data fetching with Suspense-enabled frameworks like [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) and [Next.js](https://nextjs.org/docs/getting-started/react-essentials)
- Lazy-loading component code with [`lazy`](/reference/react/lazy)
- Reading the value of a Promise with [`use`](/reference/react/use)

Suspense **does not** detect when data is fetched inside an Effect or event handler.

The exact way you would load data in the `Posts` component above depends on your framework. If you use a Suspense-enabled framework, you'll find the details in its data fetching documentation.

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.
Only data read from a source that [activates a Suspense boundary](/reference/react/Suspense#what-activates-a-suspense-boundary), such as a Promise read with [`use`](/reference/react/use), will suspend during rendering. Suspense does not detect data fetched inside an Effect or event handler.

</Note>

Expand Down
12 changes: 1 addition & 11 deletions src/content/reference/react-dom/server/renderToReadableStream.md
Original file line number Diff line number Diff line change
Expand Up @@ -283,17 +283,7 @@ Streaming does not need to wait for React itself to load in the browser, or for

<Note>

**Only Suspense-enabled data sources will activate the Suspense component.** They include:

- Data fetching with Suspense-enabled frameworks like [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) and [Next.js](https://nextjs.org/docs/getting-started/react-essentials)
- Lazy-loading component code with [`lazy`](/reference/react/lazy)
- Reading the value of a Promise with [`use`](/reference/react/use)

Suspense **does not** detect when data is fetched inside an Effect or event handler.

The exact way you would load data in the `Posts` component above depends on your framework. If you use a Suspense-enabled framework, you'll find the details in its data fetching documentation.

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.
Only data read from a source that [activates a Suspense boundary](/reference/react/Suspense#what-activates-a-suspense-boundary), such as a Promise read with [`use`](/reference/react/use), will suspend during rendering. Suspense does not detect data fetched inside an Effect or event handler.

</Note>

Expand Down
12 changes: 1 addition & 11 deletions src/content/reference/react-dom/static/prerender.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,17 +275,7 @@ Imagine that `<Posts />` needs to load some data, which takes some time. Ideally

<Note>

**Only Suspense-enabled data sources will activate the Suspense component.** They include:

- Data fetching with Suspense-enabled frameworks like [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) and [Next.js](https://nextjs.org/docs/getting-started/react-essentials)
- Lazy-loading component code with [`lazy`](/reference/react/lazy)
- Reading the value of a Promise with [`use`](/reference/react/use)

Suspense **does not** detect when data is fetched inside an Effect or event handler.

The exact way you would load data in the `Posts` component above depends on your framework. If you use a Suspense-enabled framework, you'll find the details in its data fetching documentation.

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.
Only data read from a source that [activates a Suspense boundary](/reference/react/Suspense#what-activates-a-suspense-boundary), such as a Promise read with [`use`](/reference/react/use), will suspend during rendering. Suspense does not detect data fetched inside an Effect or event handler.

</Note>

Expand Down
12 changes: 1 addition & 11 deletions src/content/reference/react-dom/static/prerenderToNodeStream.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,17 +276,7 @@ Imagine that `<Posts />` needs to load some data, which takes some time. Ideally

<Note>

**Only Suspense-enabled data sources will activate the Suspense component.** They include:

- Data fetching with Suspense-enabled frameworks like [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) and [Next.js](https://nextjs.org/docs/getting-started/react-essentials)
- Lazy-loading component code with [`lazy`](/reference/react/lazy)
- Reading the value of a Promise with [`use`](/reference/react/use)

Suspense **does not** detect when data is fetched inside an Effect or event handler.

The exact way you would load data in the `Posts` component above depends on your framework. If you use a Suspense-enabled framework, you'll find the details in its data fetching documentation.

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.
Only data read from a source that [activates a Suspense boundary](/reference/react/Suspense#what-activates-a-suspense-boundary), such as a Promise read with [`use`](/reference/react/use), will suspend during rendering. Suspense does not detect data fetched inside an Effect or event handler.

</Note>

Expand Down
12 changes: 1 addition & 11 deletions src/content/reference/react/Activity.md
Original file line number Diff line number Diff line change
Expand Up @@ -755,17 +755,7 @@ Pre-rendering components with hidden Activity boundaries is a powerful way to re

<Note>

**Only Suspense-enabled data sources will be fetched during pre-rendering.** They include:

- Data fetching with Suspense-enabled frameworks like [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) and [Next.js](https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming#streaming-with-suspense)
- Lazy-loading component code with [`lazy`](/reference/react/lazy)
- Reading the value of a cached Promise with [`use`](/reference/react/use)

Activity **does not** detect data that is fetched inside an Effect.

The exact way you would load data in the `Posts` component above depends on your framework. If you use a Suspense-enabled framework, you'll find the details in its data fetching documentation.

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.
Only data read from a source that [activates a Suspense boundary](/reference/react/Suspense#what-activates-a-suspense-boundary), such as a Promise read with [`use`](/reference/react/use), is fetched during pre-rendering. Activity does not detect data fetched inside an Effect.

</Note>

Expand Down
217 changes: 209 additions & 8 deletions src/content/reference/react/Suspense.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ title: <Suspense>

#### Caveats {/*caveats*/}

- Suspense does not detect when data is fetched inside an Effect or event handler. It only activates for the [resources listed below.](#what-activates-a-suspense-boundary)
- 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`](/reference/react/startTransition) or [`useDeferredValue`](/reference/react/useDeferredValue).
- React reveals suspended content at most once every 300ms. When a boundary's content is ready, React waits until 300ms have passed since the last reveal before showing it, so that boundaries resolving within that window are [revealed together](/blog/2025/10/01/react-19-2#batching-suspense-boundaries-for-ssr) rather than one at a time.
- If React needs to hide the already visible content because it suspended again, it will clean up [layout Effects](/reference/react/useLayoutEffect) in the content tree. When the content is ready to be shown again, React will fire the layout Effects again. This ensures 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I opened issue #8490 about the need for a caveat regarding React's batching of Suspense content, which has resulted in several issues in the React repo that can be solved with documentation. If we can add it to this pr, it would be great. I am thinking something like this:

Suggested change
- React waits at least ~300ms before revealing a `Suspense` fallback, and applies the same throttle across different boundaries. This reduces flicker when nested boundaries resolve in quick succession, which is common during [streaming server rendering](/reference/react-dom/server). Content that becomes ready within that window is held and revealed together once the window closes. A boundary that resolves before its fallback is committed—for example, synchronously, or within roughly 10ms—may skip the fallback entirely.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eps1lon can you verify this number?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

d34d3ce added here but happy to revert

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React waits at least 300ms before replacing the fallback with new content. That's different to blocking display of a fallback which only happens during updates in Transitions.

The idea is that filling in content more often leads to a "popcorn" effect. Imagine a 10x10 grid where each cell pops in 50ms intervals instead of doing 2-3 reveals including multiple cells. 300ms is just a heuristic that allows batching reveal of multiple boundaries.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplified 16fa091 and added link 54958a7

not quite sure i understood what you wanted though

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like your caveat much better than my suggestion (great job!). But I feel like it implies that suspense will always show a fallback.

My understanding is that
0ms to ~10 ms - no fallback; suspense just renders children
11ms to ~300ms - batch at call; all render all together after 300ms
~310ms+ render in 300ms windows (not sure about this one)

This is figuring out an ideal rendering schedule; it is very impressive work by the React team, but is a little hard to explain. I hope I am not derailing the goals of this pr.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would need @eps1lon here to confirm and define what docs should include

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The throttling heuristic always blocks new boundary reveals counting from the last reveal. It has no impact on whether we display a fallback or not which is decided independently (always show fallback on mount, stay on content on update).

The very first boundary React will always reveal asap. If the boundary suspended, React shows a fallback immediately. When the boundary unsuspends, React will not reveal the boundaries content before 300ms have elapsed since the last reveal.

Throttling is surfaced in the performance tracks added by React.

Consider this example (using https://codesandbox.io/p/sandbox/quiet-http-yplr4n):

function App({ p1, p2, p3 }) {
  return (
    <main>
      <h1>document</h1>
      <p>shell </p>
      <Suspense fallback="Loading lvl 1">
        <p>boundary lvl</p>
        <Suspender promise={p1} />
        <Suspense fallback="Loading lvl 2">
          <p>boundary lvl2</p>
          <Suspender promise={p2} />
          <Suspense fallback="Loading lvl 3">
            <p>boundary lvl3</p>
            <Suspender promise={p3} />
          </Suspense>
        </Suspense>
      </Suspense>
    </main>
  );
}

All Promises start at the same time as we can commit shell (which doesn't suspend and is committed instantly).

p1 resolves at 100ms
p2 resolves at 450ms
p3 resolves at 850ms

When p1 resolves we committed the root boundary (what we commonly refer to as "the shell") 100ms earlier. Since we don't want to reveal more often than every 300ms, we throttle for 200ms and reveal lvl 1 at 300ms.
When p2 resolves at 450ms, lvl1 was revealed 150ms earlier. Since we don't want to reveal more often than every 300ms, we throttle for 150ms and reveal lvl 2 at 600ms.
When p3 resolves at 850ms, lv2 was revealed 250ms earlier. Since we don't want to reveal more often than every 300ms, we throttle for 50ms and reveal lvl 2 at 900ms.

CleanShot.2026-07-01.at.14.59.48.mp4

The batching can be observed when you let two boundaries resolve within the same 300ms window e.g.
p1 resolves at 100ms
p2 resolves at 350ms
p3 resolves at 450ms

p1 is throttled for 200ms
p2 is throttled for 250ms
p3 is throttled for 150ms

p2 and p3 are revealed together.

CleanShot.2026-07-01.at.15.05.25.mp4

Without throttling all would commit independently within a short timeframe.

Throttling is global so it doesn't just apply to parent-child reveals. Initially, React throttled to 500ms which got reduced to 300ms in React 19. In React 19 we also applied throttling more broadly. The initial version of Suspense revealed boundaries immediately if there were no more nested boundaries.

Expand Down Expand Up @@ -203,22 +205,221 @@ async function getAlbums() {

</Sandpack>

<Note>
---

### What activates a Suspense boundary {/*what-activates-a-suspense-boundary*/}

@MaxwellCohen MaxwellCohen Jun 27, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very excited to see these being called out and updated!

My understanding is that a "suspense enabled framework" is a framework that wraps these features that activates a suspense boundaries . If that is true could we specifically call it out what suspense enabled means?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this? 021f94d


**Only Suspense-enabled data sources will activate the Suspense component.** They include:
A Suspense boundary waits for its content to be ready before revealing it. Any of the following blocks a boundary's content from being revealed:

- Data fetching with Suspense-enabled frameworks like [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) and [Next.js](https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming#streaming-with-suspense)
- Lazy-loading component code with [`lazy`](/reference/react/lazy)
- Reading the value of a cached Promise with [`use`](/reference/react/use)
- Lazy-loading component code with [`lazy`](/reference/react/lazy).
- Reading a Promise with [`use`](/reference/react/use), including data streamed from [Server Components](/reference/rsc/server-components) and integrations from frameworks like [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/).
- Loading a stylesheet rendered with [`<link rel="stylesheet">` and a `precedence` prop.](/reference/react-dom/components/link#special-rendering-behavior) React blocks the boundary until the stylesheet loads, up to a timeout.
- Loading fonts. React blocks a streamed boundary until [`document.fonts.ready`](https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/ready) resolves, up to a timeout. Fonts also block a [`<ViewTransition>`](/reference/react/ViewTransition) update.
Comment thread
aurorascharff marked this conversation as resolved.
Outdated
- Streaming a large boundary's HTML during server rendering. React reveals the content as the HTML arrives.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other examples are probably self-explanatory. This one could do with an example. Examples for all would be nice with a side-by-side with a Suspense implementation that wouldn't block (e.g. with just some simple DOM operations).

@aurorascharff aurorascharff Jun 30, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about with effect as an example? It's a common confusion

CleanShot 2026-06-30 at 11 46 52@2x

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's for use() vs fetching in an Effect though. I was thinking about a boundary with a lot of text. There are sandboxes in the prerender docs (or resume. Can't remember but we do have one example for PPR in the docs) that show how to build sandboxes showing streaming SSR.

@aurorascharff aurorascharff Jul 1, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. I must have misunderstood. I'm wondering if the example you are referring to is too low level for the suspense page, but would fit nicely in the prerender or resume docs. I also think keeping this current example is quite a nice way to show this common misconception. I've added now a link to those docs here, we can add the example on that page? 201b5e3 possibly in a follow-up PR

@eps1lon eps1lon Jul 2, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's any SSR API with support for streaming. The whole point for me was to explain Suspense in a single place. It isn't low-level because it explains how React thinks about Suspense.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, gotcha, will add!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

d2df82a

So I simplified my example. Still open to removing the whole use affecting if we don't think it's necessary, and then I added your example.

- Loading an image, where the `src` blocks the boundary until the image loads. This behavior is not enabled by default. When enabled, an `onLoad` handler opts an image out, and images in a [`<ViewTransition>`](/reference/react/ViewTransition) update opt in automatically.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This behavior is not enabled by default. When enabled,

this is confusing. when is it enabled, if it's not enabled by default?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eps1lon do you know about this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If inside ViewTransition, every images is opted in. Adding onLoad opts back out.

So priority is: default -> is inside ViewTransition? -> has onLoad?

by default it's currently disabled.

- <ExperimentalBadge /> Performing CPU-bound render work inside a `<Suspense>` boundary marked with the `defer` prop.

<Note>

Suspense **does not** detect when data is fetched inside an Effect or event handler.
#### What is a Suspense-enabled framework? {/*what-is-a-suspense-enabled-framework*/}

The exact way you would load data in the `Albums` component above depends on your framework. If you use a Suspense-enabled framework, you'll find the details in its data fetching documentation.
A *Suspense-enabled framework* reads data with [`use`](/reference/react/use) under the hood, so that reading data in a component activates the nearest boundary. The exact way you load data in the `Albums` component above depends on your framework, and you'll find the details in its data fetching documentation.
Comment thread
aurorascharff marked this conversation as resolved.
Outdated

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.
Without a framework, you can read a Promise with `use` directly, as long as the Promise is [cached so the same instance is reused across renders.](/reference/react/use#caching-promises-for-client-components)

</Note>

For example, both boundaries below are set up identically. The one on the left activates because its content reads a Promise with `use`, so it shows the `fallback` while loading. The one on the right fetches the same data inside an Effect, which Suspense can't detect, so its `fallback` never appears and the albums simply show up once the fetch resolves:

<Sandpack>

```js
import { Suspense } from 'react';
import Albums from './Albums.js';
import EffectAlbums from './EffectAlbums.js';

export default function App() {
return (
<div className="panels">
<section className="panel">
<h2>Reads a Promise with use</h2>
<Suspense fallback={<Loading />}>
<Albums artistId="the-beatles" />
</Suspense>
</section>
<section className="panel">
<h2>Fetches in an Effect</h2>
<Suspense fallback={<Loading />}>
<EffectAlbums artistId="the-beatles" />
</Suspense>
</section>
</div>
);
}

function Loading() {
return <p>🌀 Loading...</p>;
}
```

```js src/Albums.js active
import { use } from 'react';
import { fetchData } from './data.js';

export default function Albums({ artistId }) {
// Reading the Promise with `use` activates
// the Suspense boundary while it loads.
const albums = use(fetchData(`/${artistId}/albums`));
return (
<ul>
{albums.map(album => (
<li key={album.id}>
{album.title} ({album.year})
</li>
))}
</ul>
);
}
```

```js src/EffectAlbums.js
import { useState, useEffect } from 'react';
import { fetchData } from './data.js';

export default function EffectAlbums({ artistId }) {
const [albums, setAlbums] = useState([]);

useEffect(() => {
let active = true;
fetchData(`/${artistId}/albums`).then(result => {
if (active) {
setAlbums(result);
}
});
return () => {
active = false;
};
}, [artistId]);

// Suspense can't see this fetch, so its fallback never
// shows. The list stays empty until the data arrives.
return (
<ul>
{albums.map(album => (
<li key={album.id}>
{album.title} ({album.year})
</li>
))}
</ul>
);
}
```

```js src/data.js hidden
// Note: the way you would do data fetching depends on
// the framework that you use together with Suspense.
// Normally, the caching logic would be inside a framework.

let cache = new Map();

export function fetchData(url) {
if (!cache.has(url)) {
cache.set(url, getData(url));
}
return cache.get(url);
}

async function getData(url) {
if (url === '/the-beatles/albums') {
return await getAlbums();
} else {
throw Error('Not implemented');
}
}

async function getAlbums() {
// Add a fake delay to make waiting noticeable.
await new Promise(resolve => {
setTimeout(resolve, 3000);
});

return [{
id: 13,
title: 'Let It Be',
year: 1970
}, {
id: 12,
title: 'Abbey Road',
year: 1969
}, {
id: 11,
title: 'Yellow Submarine',
year: 1969
}, {
id: 10,
title: 'The Beatles',
year: 1968
}, {
id: 9,
title: 'Magical Mystery Tour',
year: 1967
}, {
id: 8,
title: 'Sgt. Pepper\'s Lonely Hearts Club Band',
year: 1967
}, {
id: 7,
title: 'Revolver',
year: 1966
}, {
id: 6,
title: 'Rubber Soul',
year: 1965
}, {
id: 5,
title: 'Help!',
year: 1965
}, {
id: 4,
title: 'Beatles For Sale',
year: 1964
}, {
id: 3,
title: 'A Hard Day\'s Night',
year: 1964
}, {
id: 2,
title: 'With The Beatles',
year: 1963
}, {
id: 1,
title: 'Please Please Me',
year: 1963
}];
}
```

```css
.panels {
display: flex;
gap: 20px;
}

.panel {
flex: 1;
border: 1px solid #aaa;
border-radius: 6px;
padding: 10px;
}

.panel h2 {
margin-top: 0;
font-size: 1rem;
}
```

</Sandpack>

---

### Revealing content together at once {/*revealing-content-together-at-once*/}
Expand Down
4 changes: 2 additions & 2 deletions src/content/reference/react/use.md
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ function Albums() {
}
```

Instead, pass a Promise from a cache, a Suspense-enabled framework, or a Server Component:
Instead, pass a Promise from a cache, a [Suspense-enabled framework](/reference/react/Suspense#what-is-a-suspense-enabled-framework), or a Server Component:

```js
// ✅ fetchData reads the Promise from a cache.
Expand Down Expand Up @@ -538,7 +538,7 @@ The `fetchData` function returns the same Promise each time it's called with the

<Note>

The way you cache Promises depends on the framework you use with Suspense. Frameworks typically provide built-in caching mechanisms. If you don't use a framework, you can use a simple module-level cache like the one above, or a [Suspense-enabled data source](/reference/react/Suspense#displaying-a-fallback-while-content-is-loading).
The way you cache Promises depends on the framework you use with Suspense. Frameworks typically provide built-in caching mechanisms. If you don't use a framework, you can use a simple module-level cache like the one above, or a [Suspense-enabled data source](/reference/react/Suspense#what-is-a-suspense-enabled-framework).

</Note>

Expand Down
2 changes: 1 addition & 1 deletion src/content/reference/react/useDeferredValue.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ During updates, the <CodeStep step={2}>deferred value</CodeStep> will "lag behin

<Note>

This example assumes you use a Suspense-enabled data source:
This example assumes you use a [Suspense-enabled data source](/reference/react/Suspense#what-is-a-suspense-enabled-framework):

- Data fetching with Suspense-enabled frameworks like [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/) and [Next.js](https://nextjs.org/docs/app/getting-started/fetching-data#with-suspense)
- Lazy-loading component code with [`lazy`](/reference/react/lazy)
Expand Down
Loading