Skip to content

Commit ac61e37

Browse files
[Suspense] Tune demo heights and section copy (#8526)
* Tune Suspense demo heights, data delay, and DOM comparison wording * Make section openers stand alone and restore demo timing * Shrink image demo portraits so both cards fit without layout shift * Name the resources in the combined section opener * State the ViewTransition condition in the combined section opener * Align combined section opener with sibling sections * Frame font and image waiting as Suspense behavior gated on ViewTransition * Match the combined section opener to its sibling sections * Document the experimental defer prop * Reduce the combined example's data delay * tweak
1 parent 26906f2 commit ac61e37

1 file changed

Lines changed: 19 additions & 18 deletions

File tree

src/content/reference/react/Suspense.md

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ title: <Suspense>
2626
#### Props {/*props*/}
2727
* `children`: The actual UI you intend to render. If `children` suspends while rendering, the Suspense boundary will switch to rendering `fallback`.
2828
* `fallback`: An alternate UI to render in place of the actual UI if it has not finished loading. Any valid React node is accepted, though in practice, a fallback is a lightweight placeholder view, such as a loading spinner or skeleton. Suspense will automatically switch to `fallback` when `children` suspends, and back to `children` when the data is ready. If `fallback` suspends while rendering, it will activate the closest parent Suspense boundary.
29+
* <ExperimentalBadge /> **optional** `defer`: A boolean. When `true`, React may show the `fallback` first and render or stream `children` later, even when nothing in them suspends. Use it for content that is expensive to render. Defaults to `false`.
2930

3031
#### Caveats {/*caveats*/}
3132

@@ -48,7 +49,7 @@ A Suspense boundary waits for its content to be ready before revealing it. Any o
4849
- Waiting for a large boundary's HTML to arrive during streaming server rendering. Sending HTML takes time, so a boundary with enough content activates even when nothing in it suspends. React reveals the content as the HTML arrives.
4950
- <CanaryBadge /> Loading fonts. Suspense doesn't wait for fonts by default, but a [`<ViewTransition>`](/reference/react/ViewTransition) update waits for new fonts to load, up to a timeout, so text doesn't flash with a fallback font. [See an example below.](#waiting-for-a-font-to-load)
5051
- <CanaryBadge /> Loading images. Suspense doesn't wait for images by default, but during a [`<ViewTransition>`](/reference/react/ViewTransition) update, React blocks the boundary until the image loads, up to a timeout. Adding an `onLoad` handler opts a specific image out. [See an example below.](#waiting-for-an-image-to-load)
51-
- <ExperimentalBadge /> Performing CPU-bound render work inside a `<Suspense>` boundary marked with the `defer` prop.
52+
- <ExperimentalBadge /> Performing CPU-bound render work inside a [`<Suspense defer>`](#props) boundary.
5253

5354
<Note>
5455

@@ -2379,7 +2380,7 @@ A stylesheet rendered with [`<link rel="stylesheet">` and a `precedence` prop](/
23792380
23802381
In the example below, the `Card` component renders a stylesheet with `precedence`. Press "Show card": React shows the fallback until the stylesheet has loaded, and then reveals the card with its styles applied.
23812382
2382-
For comparison, the second button performs the same update with plain DOM in a separate document, without React. Nothing waits for the stylesheet, so the card's text appears in a fallback font first and then switches:
2383+
For comparison, the second button performs the same update without React, in a separate document. Nothing waits for the stylesheet, so the card's text appears in a fallback font first and then switches:
23832384
23842385
<Sandpack>
23852386
@@ -2449,7 +2450,7 @@ export default function VanillaCard() {
24492450
}
24502451
return (
24512452
<>
2452-
<button onClick={show}>Show card (vanilla DOM)</button>
2453+
<button onClick={show}>Show card (without React)</button>
24532454
<iframe ref={ref} title="Vanilla card" className="vanilla-frame" />
24542455
</>
24552456
);
@@ -2740,11 +2741,11 @@ Where you place the `<ViewTransition>` relative to the boundary determines wheth
27402741
27412742
### <CanaryBadge /> Waiting for a font to load {/*waiting-for-a-font-to-load*/}
27422743
2743-
When a [`<ViewTransition>`](/reference/react/ViewTransition) animates a Suspense boundary's reveal, React also waits for new fonts the content introduces, up to a timeout, so the text doesn't flash with a fallback font. This only happens during a `<ViewTransition>` update.
2744+
When a [`<ViewTransition>`](/reference/react/ViewTransition) animates a Suspense boundary's reveal, React waits for new fonts the content introduces, up to a timeout, so the text doesn't flash with a fallback font. This only happens during a `<ViewTransition>` update.
27442745
27452746
In the example below, the Suspense boundary is wrapped in a `<ViewTransition>`, and the `Quote` component suspends while its data loads. Rendering the quote starts its font download. React keeps the fallback visible until the font has loaded, so the quote appears already in its font.
27462747
2747-
For comparison, the second button performs the same update with plain DOM, without React. Nothing waits for the font, so the text appears in a fallback font first and then switches:
2748+
For comparison, the second button performs the same update without React. Nothing waits for the font, so the text appears in a fallback font first and then switches:
27482749
27492750
<Sandpack>
27502751
@@ -2814,7 +2815,7 @@ export default function VanillaQuote() {
28142815
}
28152816
return (
28162817
<>
2817-
<button onClick={show}>Show quote (vanilla DOM)</button>
2818+
<button onClick={show}>Show quote (without React)</button>
28182819
<div ref={ref} />
28192820
</>
28202821
);
@@ -2890,11 +2891,11 @@ hr {
28902891
28912892
### <CanaryBadge /> Waiting for an image to load {/*waiting-for-an-image-to-load*/}
28922893
2893-
Images work the same way: when a [`<ViewTransition>`](/reference/react/ViewTransition) animates a Suspense boundary's reveal, React waits for visible images to load, up to a timeout, so the animation doesn't start with a half-loaded image. This only happens during a `<ViewTransition>` update. Adding an `onLoad` handler opts a specific image out, even inside a `<ViewTransition>`.
2894+
When a [`<ViewTransition>`](/reference/react/ViewTransition) animates a Suspense boundary's reveal, React waits for visible images to load, up to a timeout, so the animation doesn't start with a half-loaded image. This only happens during a `<ViewTransition>` update. Adding an `onLoad` handler opts a specific image out, even inside a `<ViewTransition>`.
28942895
28952896
In the example below, the Suspense boundary is wrapped in a `<ViewTransition>` and shows a profile skeleton until the portrait has loaded.
28962897
2897-
For comparison, the second button performs the same update with plain DOM, without React. Nothing waits for the image, so the card appears immediately and the image pops in when it loads:
2898+
For comparison, the second button performs the same update without React. Nothing waits for the image, so the card appears immediately and the image pops in when it loads:
28982899
28992900
<Sandpack>
29002901
@@ -2906,7 +2907,7 @@ import VanillaProfile from './VanillaProfile.js';
29062907
function Profile({ src }) {
29072908
return (
29082909
<div className="card">
2909-
<img src={src} alt="Jack Pope" width={100} height={100} />
2910+
<img src={src} alt="Jack Pope" width={80} height={80} />
29102911
<p>Jack Pope</p>
29112912
</div>
29122913
);
@@ -2955,13 +2956,13 @@ export default function VanillaProfile() {
29552956
const ref = useRef(null);
29562957
function show() {
29572958
ref.current.innerHTML = `<div class="card">
2958-
<img src="${freshImageUrl()}" alt="Jack Pope" width="100" height="100" />
2959+
<img src="${freshImageUrl()}" alt="Jack Pope" width="80" height="80" />
29592960
<p>Jack Pope</p>
29602961
</div>`;
29612962
}
29622963
return (
29632964
<>
2964-
<button onClick={show}>Show profile (vanilla DOM)</button>
2965+
<button onClick={show}>Show profile (without React)</button>
29652966
<div ref={ref} />
29662967
</>
29672968
);
@@ -2978,7 +2979,7 @@ export function freshImageUrl() {
29782979
29792980
```css
29802981
#root {
2981-
min-height: 440px;
2982+
min-height: 390px;
29822983
}
29832984
.card {
29842985
margin-top: 1em;
@@ -2992,8 +2993,8 @@ export function freshImageUrl() {
29922993
font-weight: bold;
29932994
}
29942995
.avatar-placeholder {
2995-
width: 100px;
2996-
height: 100px;
2996+
width: 80px;
2997+
height: 80px;
29972998
border-radius: 50%;
29982999
background: #dfe3e9;
29993000
}
@@ -3023,9 +3024,9 @@ hr {
30233024
30243025
### <CanaryBadge /> Coordinating fonts, images, and stylesheets {/*coordinating-fonts-images-and-stylesheets*/}
30253026
3026-
All of these waits work together. In the example below, the `ProfileCard` component suspends while its data loads, and renders a stylesheet with `precedence`, text in a new font, and a portrait. React keeps the skeleton visible while the data and the stylesheet load. The `<ViewTransition>` reveal then waits for the font and the image, so the card appears complete.
3027+
A Suspense boundary can wait for data, stylesheets, fonts, and images at once. Waiting for fonts and images only happens during a [`<ViewTransition>`](/reference/react/ViewTransition) update. In the example below, the `ProfileCard` component suspends while its data loads, and renders a stylesheet with `precedence`, text in a new font, and a portrait. React keeps the skeleton visible while the data and the stylesheet load. The `<ViewTransition>` reveal then waits for the font and the image, so the card appears complete.
30273028
3028-
For comparison, the plain DOM version loads the same data and shows every resource arriving on its own schedule:
3029+
For comparison, the version without React loads the same data and shows every resource arriving on its own schedule:
30293030
30303031
<Sandpack>
30313032
@@ -3125,7 +3126,7 @@ export default function VanillaProfileCard() {
31253126
}
31263127
return (
31273128
<>
3128-
<button onClick={show}>Show profile (vanilla DOM)</button>
3129+
<button onClick={show}>Show profile (without React)</button>
31293130
<iframe ref={ref} title="Vanilla profile card" className="vanilla-frame" />
31303131
</>
31313132
);
@@ -3155,7 +3156,7 @@ export function freshImageUrl() {
31553156
export async function fetchQuote() {
31563157
// Add a fake delay to make waiting noticeable.
31573158
await new Promise((resolve) => {
3158-
setTimeout(resolve, 2000);
3159+
setTimeout(resolve, 1000);
31593160
});
31603161
return 'The best way to predict the future is to invent it.';
31613162
}

0 commit comments

Comments
 (0)