From 0f92fef974f9efe2b26bab8350241fcd49761471 Mon Sep 17 00:00:00 2001
From: Aurora Scharff
Date: Sat, 27 Jun 2026 22:53:04 +0200
Subject: [PATCH 01/18] [Suspense] Add 'What activates a Suspense boundary'
section
---
src/content/reference/react/Suspense.md | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/src/content/reference/react/Suspense.md b/src/content/reference/react/Suspense.md
index c2fc0b6ef55..3387957fe41 100644
--- a/src/content/reference/react/Suspense.md
+++ b/src/content/reference/react/Suspense.md
@@ -203,17 +203,25 @@ async function getAlbums() {
-
+---
+
+### What activates a Suspense boundary {/*what-activates-a-suspense-boundary*/}
-**Only Suspense-enabled data sources will activate the Suspense component.** They include:
+A Suspense boundary displays its `fallback` while its content is loading, and it can also wait on other resources before revealing that content. The following activate a boundary:
-- 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 [`` 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 [``](/reference/react/ViewTransition) update.
+- Streaming a large boundary's HTML during server rendering. React reveals the content as the HTML arrives.
+- Loading an image, where `img.src` blocks the boundary until the source loads. This behavior is not enabled by default. An `` handler opts out, and images in a [``](/reference/react/ViewTransition) update opt in automatically.
+- Performing CPU-bound render work inside a `` boundary marked with the `defer` prop.
+
+
Suspense **does not** detect when data is fetched inside an Effect or event handler.
-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.
+The exact way you would load data in the `Albums` component above depends on your framework. If you use a framework with built-in Suspense support, 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.
From 4ddc1e3fd3fbb8c40affa2a79dcaa15ae3690423 Mon Sep 17 00:00:00 2001
From: Aurora Scharff
Date: Sat, 27 Jun 2026 23:00:25 +0200
Subject: [PATCH 02/18] [Suspense] Clarify image activation wording (review
feedback)
---
src/content/reference/react/Suspense.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/content/reference/react/Suspense.md b/src/content/reference/react/Suspense.md
index 3387957fe41..ec15a755524 100644
--- a/src/content/reference/react/Suspense.md
+++ b/src/content/reference/react/Suspense.md
@@ -214,7 +214,7 @@ A Suspense boundary displays its `fallback` while its content is loading, and it
- Loading a stylesheet rendered with [`` 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 [``](/reference/react/ViewTransition) update.
- Streaming a large boundary's HTML during server rendering. React reveals the content as the HTML arrives.
-- Loading an image, where `img.src` blocks the boundary until the source loads. This behavior is not enabled by default. An `` handler opts out, and images in a [``](/reference/react/ViewTransition) update opt in automatically.
+- 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 [``](/reference/react/ViewTransition) update opt in automatically.
- Performing CPU-bound render work inside a `` boundary marked with the `defer` prop.
From 021f94dc847fae905d80f8d9a85f843767ddb41e Mon Sep 17 00:00:00 2001
From: Aurora Scharff
Date: Sat, 27 Jun 2026 23:45:29 +0200
Subject: [PATCH 03/18] Define "suspense-enabled-framework"
---
.../reference/react-dom/server/renderToPipeableStream.md | 2 +-
.../reference/react-dom/server/renderToReadableStream.md | 2 +-
src/content/reference/react/Activity.md | 2 +-
src/content/reference/react/Suspense.md | 5 +++--
src/content/reference/react/use.md | 4 ++--
src/content/reference/react/useDeferredValue.md | 2 +-
6 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/src/content/reference/react-dom/server/renderToPipeableStream.md b/src/content/reference/react-dom/server/renderToPipeableStream.md
index 84b8873a6fd..0ee3acba001 100644
--- a/src/content/reference/react-dom/server/renderToPipeableStream.md
+++ b/src/content/reference/react-dom/server/renderToPipeableStream.md
@@ -292,7 +292,7 @@ Streaming does not need to wait for React itself to load in the browser, or for
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.
+The exact way you would load data in the `Posts` component above depends on your framework. If you use a [Suspense-enabled framework](/reference/react/Suspense#what-is-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.
diff --git a/src/content/reference/react-dom/server/renderToReadableStream.md b/src/content/reference/react-dom/server/renderToReadableStream.md
index f3e862124af..5c083432a8f 100644
--- a/src/content/reference/react-dom/server/renderToReadableStream.md
+++ b/src/content/reference/react-dom/server/renderToReadableStream.md
@@ -291,7 +291,7 @@ Streaming does not need to wait for React itself to load in the browser, or for
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.
+The exact way you would load data in the `Posts` component above depends on your framework. If you use a [Suspense-enabled framework](/reference/react/Suspense#what-is-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.
diff --git a/src/content/reference/react/Activity.md b/src/content/reference/react/Activity.md
index 127a4b8d0ae..8027ea9e6d3 100644
--- a/src/content/reference/react/Activity.md
+++ b/src/content/reference/react/Activity.md
@@ -763,7 +763,7 @@ Pre-rendering components with hidden Activity boundaries is a powerful way to re
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.
+The exact way you would load data in the `Posts` component above depends on your framework. If you use a [Suspense-enabled framework](/reference/react/Suspense#what-is-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.
diff --git a/src/content/reference/react/Suspense.md b/src/content/reference/react/Suspense.md
index ec15a755524..518e1737a8e 100644
--- a/src/content/reference/react/Suspense.md
+++ b/src/content/reference/react/Suspense.md
@@ -29,6 +29,7 @@ title:
#### 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).
- 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.
@@ -219,9 +220,9 @@ A Suspense boundary displays its `fallback` while its content is loading, and it
-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 framework with built-in Suspense support, you'll find the details in its data fetching documentation.
+A *Suspense-enabled framework* integrates data fetching with Suspense, 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.
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.
diff --git a/src/content/reference/react/use.md b/src/content/reference/react/use.md
index 1780f82e7b5..c21462d4a3d 100644
--- a/src/content/reference/react/use.md
+++ b/src/content/reference/react/use.md
@@ -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.
@@ -538,7 +538,7 @@ The `fetchData` function returns the same Promise each time it's called with the
-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).
diff --git a/src/content/reference/react/useDeferredValue.md b/src/content/reference/react/useDeferredValue.md
index 40cb92629b5..c01250459c5 100644
--- a/src/content/reference/react/useDeferredValue.md
+++ b/src/content/reference/react/useDeferredValue.md
@@ -86,7 +86,7 @@ During updates, the deferred value will "lag behin
-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)
From 14e8f6742fdf91ca7cb483228dc74769b6210f01 Mon Sep 17 00:00:00 2001
From: Aurora Scharff
Date: Sun, 28 Jun 2026 00:14:36 +0200
Subject: [PATCH 04/18] Update outdated framework-less data fetching note to
reference use()
---
.../reference/react-dom/server/renderToPipeableStream.md | 2 +-
.../reference/react-dom/server/renderToReadableStream.md | 2 +-
src/content/reference/react-dom/static/prerender.md | 4 ++--
.../reference/react-dom/static/prerenderToNodeStream.md | 4 ++--
src/content/reference/react/Activity.md | 2 +-
src/content/reference/react/Suspense.md | 2 +-
6 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/content/reference/react-dom/server/renderToPipeableStream.md b/src/content/reference/react-dom/server/renderToPipeableStream.md
index 0ee3acba001..df148494983 100644
--- a/src/content/reference/react-dom/server/renderToPipeableStream.md
+++ b/src/content/reference/react-dom/server/renderToPipeableStream.md
@@ -294,7 +294,7 @@ Suspense **does not** detect when data is fetched inside an Effect or event hand
The exact way you would load data in the `Posts` component above depends on your framework. If you use a [Suspense-enabled framework](/reference/react/Suspense#what-is-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.
+Without a framework, you can read a Promise with [`use`](/reference/react/use) as long as the Promise is [cached so the same instance is reused across renders.](/reference/react/use#caching-promises-for-client-components)
diff --git a/src/content/reference/react-dom/server/renderToReadableStream.md b/src/content/reference/react-dom/server/renderToReadableStream.md
index 5c083432a8f..73019721ab7 100644
--- a/src/content/reference/react-dom/server/renderToReadableStream.md
+++ b/src/content/reference/react-dom/server/renderToReadableStream.md
@@ -293,7 +293,7 @@ Suspense **does not** detect when data is fetched inside an Effect or event hand
The exact way you would load data in the `Posts` component above depends on your framework. If you use a [Suspense-enabled framework](/reference/react/Suspense#what-is-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.
+Without a framework, you can read a Promise with [`use`](/reference/react/use) as long as the Promise is [cached so the same instance is reused across renders.](/reference/react/use#caching-promises-for-client-components)
diff --git a/src/content/reference/react-dom/static/prerender.md b/src/content/reference/react-dom/static/prerender.md
index 8ad47aa15f3..8192b491280 100644
--- a/src/content/reference/react-dom/static/prerender.md
+++ b/src/content/reference/react-dom/static/prerender.md
@@ -283,9 +283,9 @@ Imagine that `` needs to load some data, which takes some time. Ideally
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.
+The exact way you would load data in the `Posts` component above depends on your framework. If you use a [Suspense-enabled framework](/reference/react/Suspense#what-is-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.
+Without a framework, you can read a Promise with [`use`](/reference/react/use) as long as the Promise is [cached so the same instance is reused across renders.](/reference/react/use#caching-promises-for-client-components)
diff --git a/src/content/reference/react-dom/static/prerenderToNodeStream.md b/src/content/reference/react-dom/static/prerenderToNodeStream.md
index 7a31f66a1e4..6336b680bd9 100644
--- a/src/content/reference/react-dom/static/prerenderToNodeStream.md
+++ b/src/content/reference/react-dom/static/prerenderToNodeStream.md
@@ -284,9 +284,9 @@ Imagine that `` needs to load some data, which takes some time. Ideally
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.
+The exact way you would load data in the `Posts` component above depends on your framework. If you use a [Suspense-enabled framework](/reference/react/Suspense#what-is-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.
+Without a framework, you can read a Promise with [`use`](/reference/react/use) as long as the Promise is [cached so the same instance is reused across renders.](/reference/react/use#caching-promises-for-client-components)
diff --git a/src/content/reference/react/Activity.md b/src/content/reference/react/Activity.md
index 8027ea9e6d3..0ef8dcbef5c 100644
--- a/src/content/reference/react/Activity.md
+++ b/src/content/reference/react/Activity.md
@@ -765,7 +765,7 @@ 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](/reference/react/Suspense#what-is-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.
+Without a framework, you can read a Promise with [`use`](/reference/react/use) as long as the Promise is [cached so the same instance is reused across renders.](/reference/react/use#caching-promises-for-client-components)
diff --git a/src/content/reference/react/Suspense.md b/src/content/reference/react/Suspense.md
index 518e1737a8e..a23706f1e79 100644
--- a/src/content/reference/react/Suspense.md
+++ b/src/content/reference/react/Suspense.md
@@ -224,7 +224,7 @@ A Suspense boundary displays its `fallback` while its content is loading, and it
A *Suspense-enabled framework* integrates data fetching with Suspense, 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.
-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`](/reference/react/use) as long as the Promise is [cached so the same instance is reused across renders.](/reference/react/use#caching-promises-for-client-components)
From d34d3ce2af06de54ca8a3df2e1544075603a9d1f Mon Sep 17 00:00:00 2001
From: Aurora Scharff
Date: Tue, 30 Jun 2026 11:02:48 -0400
Subject: [PATCH 05/18] fix: clarify Suspense boundary activation and fallback
behavior
---
src/content/reference/react/Suspense.md | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/content/reference/react/Suspense.md b/src/content/reference/react/Suspense.md
index a23706f1e79..ccb7cf52d66 100644
--- a/src/content/reference/react/Suspense.md
+++ b/src/content/reference/react/Suspense.md
@@ -32,6 +32,7 @@ title:
- 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).
+- Once React shows a `fallback`, it keeps it visible for at least ~300ms, revealing any boundaries that resolve within that window together to reduce flicker during [streaming server rendering](/reference/react-dom/server). A boundary whose content is ready before its `fallback` is committed skips the `fallback` entirely.
- 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.
@@ -208,7 +209,7 @@ async function getAlbums() {
### What activates a Suspense boundary {/*what-activates-a-suspense-boundary*/}
-A Suspense boundary displays its `fallback` while its content is loading, and it can also wait on other resources before revealing that content. The following activate a boundary:
+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:
- 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/).
@@ -222,9 +223,9 @@ A Suspense boundary displays its `fallback` while its content is loading, and it
#### What is a Suspense-enabled framework? {/*what-is-a-suspense-enabled-framework*/}
-A *Suspense-enabled framework* integrates data fetching with Suspense, 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.
+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.
-Without a framework, you can read a Promise with [`use`](/reference/react/use) as long as the Promise is [cached so the same instance is reused across renders.](/reference/react/use#caching-promises-for-client-components)
+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)
From 2e01ae74b3136c0c3cc384569b9eb1fbe050694d Mon Sep 17 00:00:00 2001
From: Aurora Scharff
Date: Tue, 30 Jun 2026 11:47:18 -0400
Subject: [PATCH 06/18] Add code example
---
src/content/reference/react/Suspense.md | 190 ++++++++++++++++++++++++
1 file changed, 190 insertions(+)
diff --git a/src/content/reference/react/Suspense.md b/src/content/reference/react/Suspense.md
index ccb7cf52d66..34e8b975def 100644
--- a/src/content/reference/react/Suspense.md
+++ b/src/content/reference/react/Suspense.md
@@ -229,6 +229,196 @@ Without a framework, you can read a Promise with `use` directly, as long as the
+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:
+
+
+
+```js
+import { Suspense } from 'react';
+import Albums from './Albums.js';
+import EffectAlbums from './EffectAlbums.js';
+
+export default function App() {
+ return (
+
+
+
Reads a Promise with use
+ }>
+
+
+
+
+
Fetches in an Effect
+ }>
+
+
+
+
+ );
+}
+
+function Loading() {
+ return
🌀 Loading...
;
+}
+```
+
+```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 (
+
+ {albums.map(album => (
+
+ {album.title} ({album.year})
+
+ ))}
+
+ );
+}
+```
+
+```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 (
+
+ {albums.map(album => (
+
+ {album.title} ({album.year})
+
+ ))}
+
+ );
+}
+```
+
+```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;
+ padding: 0 15px;
+ border: 1px solid #eee;
+ border-radius: 8px;
+}
+
+.panel h2 {
+ font-size: 1rem;
+}
+```
+
+
+
---
### Revealing content together at once {/*revealing-content-together-at-once*/}
From f6d28e6c6fe615ea927b5881be395fb13d087e40 Mon Sep 17 00:00:00 2001
From: Aurora Scharff
Date: Tue, 30 Jun 2026 11:47:27 -0400
Subject: [PATCH 07/18] update styling
---
src/content/reference/react/Suspense.md | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/content/reference/react/Suspense.md b/src/content/reference/react/Suspense.md
index 34e8b975def..3bf45292ac9 100644
--- a/src/content/reference/react/Suspense.md
+++ b/src/content/reference/react/Suspense.md
@@ -407,12 +407,13 @@ async function getAlbums() {
.panel {
flex: 1;
- padding: 0 15px;
- border: 1px solid #eee;
- border-radius: 8px;
+ border: 1px solid #aaa;
+ border-radius: 6px;
+ padding: 10px;
}
.panel h2 {
+ margin-top: 0;
font-size: 1rem;
}
```
From 16fa091c826ad7bd5b7f4a50311681ac2cdd6085 Mon Sep 17 00:00:00 2001
From: Aurora Scharff
Date: Tue, 30 Jun 2026 13:28:09 -0400
Subject: [PATCH 08/18] Cleanup caveat
---
src/content/reference/react/Suspense.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/content/reference/react/Suspense.md b/src/content/reference/react/Suspense.md
index 3bf45292ac9..577bff06d1a 100644
--- a/src/content/reference/react/Suspense.md
+++ b/src/content/reference/react/Suspense.md
@@ -32,7 +32,7 @@ title:
- 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).
-- Once React shows a `fallback`, it keeps it visible for at least ~300ms, revealing any boundaries that resolve within that window together to reduce flicker during [streaming server rendering](/reference/react-dom/server). A boundary whose content is ready before its `fallback` is committed skips the `fallback` entirely.
+- Once React shows a `fallback`, it waits at least 300ms before replacing it with the resolved content, so that boundaries resolving within that window are revealed together 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.
From 54958a759677506b6b8d55f0291b5e95324d4f83 Mon Sep 17 00:00:00 2001
From: Aurora Scharff
Date: Tue, 30 Jun 2026 13:29:57 -0400
Subject: [PATCH 09/18] add link to blogpost
---
src/content/reference/react/Suspense.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/content/reference/react/Suspense.md b/src/content/reference/react/Suspense.md
index 577bff06d1a..91ed2d2ae90 100644
--- a/src/content/reference/react/Suspense.md
+++ b/src/content/reference/react/Suspense.md
@@ -32,7 +32,7 @@ title:
- 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).
-- Once React shows a `fallback`, it waits at least 300ms before replacing it with the resolved content, so that boundaries resolving within that window are revealed together rather than one at a time.
+- Once React shows a `fallback`, it waits at least 300ms before replacing it with the resolved content, 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.
From 6f6185d1ba68986e7c495007ef30af32e4f98a10 Mon Sep 17 00:00:00 2001
From: Aurora Scharff
Date: Wed, 1 Jul 2026 08:14:27 -0400
Subject: [PATCH 10/18] Replace notes with link to suspense docs
---
.../react-dom/server/renderToPipeableStream.md | 12 +-----------
.../react-dom/server/renderToReadableStream.md | 12 +-----------
src/content/reference/react-dom/static/prerender.md | 12 +-----------
.../react-dom/static/prerenderToNodeStream.md | 12 +-----------
src/content/reference/react/Activity.md | 12 +-----------
5 files changed, 5 insertions(+), 55 deletions(-)
diff --git a/src/content/reference/react-dom/server/renderToPipeableStream.md b/src/content/reference/react-dom/server/renderToPipeableStream.md
index df148494983..520d7e6a30c 100644
--- a/src/content/reference/react-dom/server/renderToPipeableStream.md
+++ b/src/content/reference/react-dom/server/renderToPipeableStream.md
@@ -284,17 +284,7 @@ Streaming does not need to wait for React itself to load in the browser, or for
-**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](/reference/react/Suspense#what-is-a-suspense-enabled-framework), you'll find the details in its data fetching documentation.
-
-Without a framework, you can read a Promise with [`use`](/reference/react/use) as long as the Promise is [cached so the same instance is reused across renders.](/reference/react/use#caching-promises-for-client-components)
+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.
diff --git a/src/content/reference/react-dom/server/renderToReadableStream.md b/src/content/reference/react-dom/server/renderToReadableStream.md
index 73019721ab7..d90a03f2bae 100644
--- a/src/content/reference/react-dom/server/renderToReadableStream.md
+++ b/src/content/reference/react-dom/server/renderToReadableStream.md
@@ -283,17 +283,7 @@ Streaming does not need to wait for React itself to load in the browser, or for
-**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](/reference/react/Suspense#what-is-a-suspense-enabled-framework), you'll find the details in its data fetching documentation.
-
-Without a framework, you can read a Promise with [`use`](/reference/react/use) as long as the Promise is [cached so the same instance is reused across renders.](/reference/react/use#caching-promises-for-client-components)
+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.
diff --git a/src/content/reference/react-dom/static/prerender.md b/src/content/reference/react-dom/static/prerender.md
index 8192b491280..7f241c4926d 100644
--- a/src/content/reference/react-dom/static/prerender.md
+++ b/src/content/reference/react-dom/static/prerender.md
@@ -275,17 +275,7 @@ Imagine that `` needs to load some data, which takes some time. Ideally
-**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](/reference/react/Suspense#what-is-a-suspense-enabled-framework), you'll find the details in its data fetching documentation.
-
-Without a framework, you can read a Promise with [`use`](/reference/react/use) as long as the Promise is [cached so the same instance is reused across renders.](/reference/react/use#caching-promises-for-client-components)
+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.
diff --git a/src/content/reference/react-dom/static/prerenderToNodeStream.md b/src/content/reference/react-dom/static/prerenderToNodeStream.md
index 6336b680bd9..4bef38e3831 100644
--- a/src/content/reference/react-dom/static/prerenderToNodeStream.md
+++ b/src/content/reference/react-dom/static/prerenderToNodeStream.md
@@ -276,17 +276,7 @@ Imagine that `` needs to load some data, which takes some time. Ideally
-**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](/reference/react/Suspense#what-is-a-suspense-enabled-framework), you'll find the details in its data fetching documentation.
-
-Without a framework, you can read a Promise with [`use`](/reference/react/use) as long as the Promise is [cached so the same instance is reused across renders.](/reference/react/use#caching-promises-for-client-components)
+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.
diff --git a/src/content/reference/react/Activity.md b/src/content/reference/react/Activity.md
index 0ef8dcbef5c..b521970b764 100644
--- a/src/content/reference/react/Activity.md
+++ b/src/content/reference/react/Activity.md
@@ -755,17 +755,7 @@ Pre-rendering components with hidden Activity boundaries is a powerful way to re
-**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](/reference/react/Suspense#what-is-a-suspense-enabled-framework), you'll find the details in its data fetching documentation.
-
-Without a framework, you can read a Promise with [`use`](/reference/react/use) as long as the Promise is [cached so the same instance is reused across renders.](/reference/react/use#caching-promises-for-client-components)
+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.
From b7c20940ebbf48cf3b0cb0370dce1d8f46698bde Mon Sep 17 00:00:00 2001
From: Aurora Scharff
Date: Wed, 1 Jul 2026 09:37:32 -0400
Subject: [PATCH 11/18] Tweak caveat
---
src/content/reference/react/Suspense.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/content/reference/react/Suspense.md b/src/content/reference/react/Suspense.md
index 91ed2d2ae90..5665b214469 100644
--- a/src/content/reference/react/Suspense.md
+++ b/src/content/reference/react/Suspense.md
@@ -32,7 +32,7 @@ title:
- 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).
-- Once React shows a `fallback`, it waits at least 300ms before replacing it with the resolved content, 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.
+- 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.
From 201b5e35d0e01a9b970988031ac3e68385ada275 Mon Sep 17 00:00:00 2001
From: Aurora Scharff
Date: Wed, 1 Jul 2026 17:58:02 -0400
Subject: [PATCH 12/18] Update caveat to clarify streaming behavior during
server rendering
---
src/content/reference/react/Suspense.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/content/reference/react/Suspense.md b/src/content/reference/react/Suspense.md
index 5665b214469..ae06874721a 100644
--- a/src/content/reference/react/Suspense.md
+++ b/src/content/reference/react/Suspense.md
@@ -215,7 +215,7 @@ A Suspense boundary waits for its content to be ready before revealing it. Any o
- 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 [`` 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 [``](/reference/react/ViewTransition) update.
-- Streaming a large boundary's HTML during server rendering. React reveals the content as the HTML arrives.
+- Streaming a large boundary's HTML during server rendering. React [reveals the content as the HTML arrives.](/reference/react-dom/server/renderToReadableStream#streaming-more-content-as-it-loads)
- 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 [``](/reference/react/ViewTransition) update opt in automatically.
- Performing CPU-bound render work inside a `` boundary marked with the `defer` prop.
From d2df82afcd0fafc81afdc3b4ed1ea96a4bc8a06a Mon Sep 17 00:00:00 2001
From: Aurora Scharff
Date: Thu, 2 Jul 2026 10:59:41 -0400
Subject: [PATCH 13/18] Address feedback
---
src/content/reference/react/Suspense.md | 129 ++++++++++++++----------
1 file changed, 78 insertions(+), 51 deletions(-)
diff --git a/src/content/reference/react/Suspense.md b/src/content/reference/react/Suspense.md
index ae06874721a..6d577a8f079 100644
--- a/src/content/reference/react/Suspense.md
+++ b/src/content/reference/react/Suspense.md
@@ -214,7 +214,7 @@ A Suspense boundary waits for its content to be ready before revealing it. Any o
- 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 [`` 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 [``](/reference/react/ViewTransition) update.
+- Loading fonts. When a boundary is revealed by streamed SSR content, React waits for [`document.fonts.ready`](https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/ready) before showing it, up to a timeout, so text doesn't flash with a fallback font. Fonts also block a [``](/reference/react/ViewTransition) update.
- Streaming a large boundary's HTML during server rendering. React [reveals the content as the HTML arrives.](/reference/react-dom/server/renderToReadableStream#streaming-more-content-as-it-loads)
- 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 [``](/reference/react/ViewTransition) update opt in automatically.
- Performing CPU-bound render work inside a `` boundary marked with the `defer` prop.
@@ -229,60 +229,28 @@ Without a framework, you can read a Promise with `use` directly, as long as the
-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:
+Fetching data inside an Effect does not activate the boundary. Suspense can't detect the fetch, so the `fallback` never appears and the list stays empty until the data arrives:
```js
import { Suspense } from 'react';
-import Albums from './Albums.js';
import EffectAlbums from './EffectAlbums.js';
export default function App() {
return (
-
-
-
Reads a Promise with use
- }>
-
-
-
-
-
Fetches in an Effect
- }>
-
-
-
-
+ }>
+
+
);
}
function Loading() {
- return
🌀 Loading...
;
-}
-```
-
-```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 (
-
- {albums.map(album => (
-
- {album.title} ({album.year})
-
- ))}
-
- );
+ return
🌀 Loading...
;
}
```
-```js src/EffectAlbums.js
+```js src/EffectAlbums.js active
import { useState, useEffect } from 'react';
import { fetchData } from './data.js';
@@ -399,22 +367,81 @@ async function getAlbums() {
}
```
-```css
-.panels {
- display: flex;
- gap: 20px;
+
+
+During streaming server rendering, a boundary also activates as its HTML arrives. With any streaming SSR API, React sends the shell with the `fallback` first, then streams in each boundary's HTML and swaps out its `fallback` as that content arrives. This progressive reveal applies only to content streamed from the server, not to updates on the client:
+
+
+
+```js src/App.js hidden
+```
+
+```html public/index.html
+
+
+
+
+ Streaming SSR
+
+
+
+
+
+```
+
+```js src/index.js
+import { flushReadableStreamToFrame } from './demo-helpers.js';
+import { Suspense, use } from 'react';
+import { renderToReadableStream } from 'react-dom/server';
+
+const { promise: posts, resolve: resolvePosts } =
+ Promise.withResolvers();
+
+function Posts() {
+ const text = use(posts);
+ return
}>
+
+
+
+
+ );
}
-.panel h2 {
- margin-top: 0;
- font-size: 1rem;
+async function main(frame) {
+ const stream = await renderToReadableStream();
+
+ // The posts resolve after the shell has streamed, so React
+ // streams their HTML in and swaps out the fallback.
+ setTimeout(() => {
+ resolvePosts(
+ 'Just got back from two weeks along the coast. The drive ' +
+ 'was longer than expected, but every stop was worth it. ' +
+ 'A full write-up and more photos are coming soon.'
+ );
+ }, 1500);
+
+ await flushReadableStreamToFrame(stream, frame);
+}
+
+main(document.getElementById('container'));
+```
+
+```js src/demo-helpers.js hidden
+export async function flushReadableStreamToFrame(readable, frame) {
+ const doc = frame.contentWindow.document;
+ const decoder = new TextDecoder();
+ for await (const chunk of readable) {
+ doc.write(decoder.decode(chunk));
+ }
}
```
From b33c6378aa9d01fe61963c1bc83181a45f64a03c Mon Sep 17 00:00:00 2001
From: Aurora Scharff
Date: Thu, 2 Jul 2026 16:36:34 -0400
Subject: [PATCH 14/18] address feedback
---
src/content/reference/react/Suspense.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/content/reference/react/Suspense.md b/src/content/reference/react/Suspense.md
index 6d577a8f079..465d779ff30 100644
--- a/src/content/reference/react/Suspense.md
+++ b/src/content/reference/react/Suspense.md
@@ -215,7 +215,7 @@ A Suspense boundary waits for its content to be ready before revealing it. Any o
- 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 [`` 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. When a boundary is revealed by streamed SSR content, React waits for [`document.fonts.ready`](https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/ready) before showing it, up to a timeout, so text doesn't flash with a fallback font. Fonts also block a [``](/reference/react/ViewTransition) update.
-- Streaming a large boundary's HTML during server rendering. React [reveals the content as the HTML arrives.](/reference/react-dom/server/renderToReadableStream#streaming-more-content-as-it-loads)
+- Streaming a large boundary's HTML during server rendering. React reveals the content as the HTML arrives.
- 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 [``](/reference/react/ViewTransition) update opt in automatically.
- Performing CPU-bound render work inside a `` boundary marked with the `defer` prop.
@@ -223,7 +223,7 @@ A Suspense boundary waits for its content to be ready before revealing it. Any o
#### What is a Suspense-enabled framework? {/*what-is-a-suspense-enabled-framework*/}
-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.
+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 your data depends on your framework; you'll find the details in its documentation.
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)
From 9091bee54c33afce1b15b73272e909aafb8fc70d Mon Sep 17 00:00:00 2001
From: Aurora Scharff
Date: Thu, 2 Jul 2026 18:18:34 -0400
Subject: [PATCH 15/18] Clarify documentation on Suspense-enabled frameworks
---
src/content/reference/react/Suspense.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/content/reference/react/Suspense.md b/src/content/reference/react/Suspense.md
index 465d779ff30..2b70f36975e 100644
--- a/src/content/reference/react/Suspense.md
+++ b/src/content/reference/react/Suspense.md
@@ -223,7 +223,7 @@ A Suspense boundary waits for its content to be ready before revealing it. Any o
#### What is a Suspense-enabled framework? {/*what-is-a-suspense-enabled-framework*/}
-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 your data depends on your framework; you'll find the details in its 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 your data depends on your framework, and you'll find the details in its documentation.
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)
From 9be6345d0a5d7e71d075f10aa46645c453eff4c3 Mon Sep 17 00:00:00 2001
From: Aurora Scharff
Date: Thu, 2 Jul 2026 18:29:59 -0400
Subject: [PATCH 16/18] Update links and wording
---
src/content/reference/react/Suspense.md | 4 ++--
src/content/reference/react/use.md | 2 +-
src/content/reference/react/useDeferredValue.md | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/content/reference/react/Suspense.md b/src/content/reference/react/Suspense.md
index 2b70f36975e..fb73997a667 100644
--- a/src/content/reference/react/Suspense.md
+++ b/src/content/reference/react/Suspense.md
@@ -29,10 +29,10 @@ title:
#### 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)
+- Suspense does not detect when data is fetched inside an Effect or event handler. It only activates in the [cases 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.
+- React reveals suspended content at most once every 300ms, measured from the last reveal. Boundaries that become ready 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.
diff --git a/src/content/reference/react/use.md b/src/content/reference/react/use.md
index c21462d4a3d..17637376391 100644
--- a/src/content/reference/react/use.md
+++ b/src/content/reference/react/use.md
@@ -538,7 +538,7 @@ The `fetchData` function returns the same Promise each time it's called with the
-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).
+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-activates-a-suspense-boundary).
diff --git a/src/content/reference/react/useDeferredValue.md b/src/content/reference/react/useDeferredValue.md
index c01250459c5..72be027ea63 100644
--- a/src/content/reference/react/useDeferredValue.md
+++ b/src/content/reference/react/useDeferredValue.md
@@ -86,7 +86,7 @@ During updates, the deferred value will "lag behin
-This example assumes you use a [Suspense-enabled data source](/reference/react/Suspense#what-is-a-suspense-enabled-framework):
+This example assumes you use a [Suspense-enabled data source](/reference/react/Suspense#what-activates-a-suspense-boundary):
- 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)
From 025ee7a0d3f1d09444d7c2ee1581abdb5d89893a Mon Sep 17 00:00:00 2001
From: Aurora Scharff
Date: Fri, 3 Jul 2026 09:07:40 -0400
Subject: [PATCH 17/18] Improve example
---
src/content/reference/react/Suspense.md | 40 +++++++++++++++++++++----
1 file changed, 34 insertions(+), 6 deletions(-)
diff --git a/src/content/reference/react/Suspense.md b/src/content/reference/react/Suspense.md
index fb73997a667..dc6930f8ac1 100644
--- a/src/content/reference/react/Suspense.md
+++ b/src/content/reference/react/Suspense.md
@@ -233,15 +233,43 @@ Fetching data inside an Effect does not activate the boundary. Suspense can't de
-```js
+```js src/App.js hidden
+import { useState } from 'react';
+import ArtistPage from './ArtistPage.js';
+
+export default function App() {
+ const [show, setShow] = useState(false);
+ if (show) {
+ return (
+
+ );
+ } else {
+ return (
+
+ );
+ }
+}
+```
+
+```js src/ArtistPage.js active
import { Suspense } from 'react';
import EffectAlbums from './EffectAlbums.js';
-export default function App() {
+export default function ArtistPage({ artist }) {
return (
- }>
-
-
+ <>
+
{artist.name}
+ }>
+
+
+ >
);
}
@@ -250,7 +278,7 @@ function Loading() {
}
```
-```js src/EffectAlbums.js active
+```js src/EffectAlbums.js
import { useState, useEffect } from 'react';
import { fetchData } from './data.js';
From 2dfe10fc197b1c64761345fb7e555fcd2da74bf9 Mon Sep 17 00:00:00 2001
From: Aurora Scharff
Date: Fri, 3 Jul 2026 09:35:14 -0400
Subject: [PATCH 18/18] Clarify heading for Suspense-enabled frameworks section
---
src/content/reference/react/Suspense.md | 7 ++++---
src/content/reference/react/use.md | 2 +-
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/content/reference/react/Suspense.md b/src/content/reference/react/Suspense.md
index dc6930f8ac1..1ccd934a373 100644
--- a/src/content/reference/react/Suspense.md
+++ b/src/content/reference/react/Suspense.md
@@ -212,7 +212,8 @@ async function getAlbums() {
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:
- 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/).
+- Reading a Promise with [`use`](/reference/react/use), including data streamed from [Server Components](/reference/rsc/server-components).
+- Data fetching through a [Suspense-enabled framework](#suspense-enabled-frameworks) like [Relay](https://relay.dev/docs/guided-tour/rendering/loading-states/), which integrates its own data source with Suspense.
- Loading a stylesheet rendered with [`` 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. When a boundary is revealed by streamed SSR content, React waits for [`document.fonts.ready`](https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/ready) before showing it, up to a timeout, so text doesn't flash with a fallback font. Fonts also block a [``](/reference/react/ViewTransition) update.
- Streaming a large boundary's HTML during server rendering. React reveals the content as the HTML arrives.
@@ -221,9 +222,9 @@ A Suspense boundary waits for its content to be ready before revealing it. Any o
-#### What is a Suspense-enabled framework? {/*what-is-a-suspense-enabled-framework*/}
+#### Suspense-enabled frameworks {/*suspense-enabled-frameworks*/}
-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 your data depends on your framework, and you'll find the details in its documentation.
+A *Suspense-enabled framework* integrates its data fetching with Suspense, so that reading data in a component activates the nearest boundary. Some frameworks build on Server Components and [`use`](/reference/react/use), like Next.js, while others provide a custom integration, like Relay. The exact way you load your data depends on your framework, and you'll find the details in its documentation.
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)
diff --git a/src/content/reference/react/use.md b/src/content/reference/react/use.md
index 17637376391..611a0608aae 100644
--- a/src/content/reference/react/use.md
+++ b/src/content/reference/react/use.md
@@ -472,7 +472,7 @@ function Albums() {
}
```
-Instead, pass a Promise from a cache, a [Suspense-enabled framework](/reference/react/Suspense#what-is-a-suspense-enabled-framework), or a Server Component:
+Instead, pass a Promise from a cache, a [Suspense-enabled framework](/reference/react/Suspense#suspense-enabled-frameworks), or a Server Component:
```js
// ✅ fetchData reads the Promise from a cache.