From 6c0476a68c072bb95ef5babc09093a43ce62f696 Mon Sep 17 00:00:00 2001 From: Aurora Scharff Date: Tue, 8 Sep 2026 13:05:16 +0200 Subject: [PATCH 01/16] up --- src/content/reference/react/Activity.md | 1270 +++++------------------ 1 file changed, 237 insertions(+), 1033 deletions(-) diff --git a/src/content/reference/react/Activity.md b/src/content/reference/react/Activity.md index b521970b764..32d31ae0243 100644 --- a/src/content/reference/react/Activity.md +++ b/src/content/reference/react/Activity.md @@ -4,10 +4,10 @@ title: -`` lets you hide and restore the UI and internal state of its children. +`` lets you hide and reveal part of the UI while preserving its state. ```js - + ``` @@ -22,215 +22,148 @@ title: ### `` {/*activity*/} -You can use Activity to hide part of your application: +Wrap part of the component tree in `` to control whether it is visible: -```js [[1, 1, "\\"hidden\\""], [2, 2, ""], [3, 1, "\\"visible\\""]] - +```js +import { Activity } from 'react'; + + ``` -When an Activity boundary is hidden, React will visually hide its children using the `display: "none"` CSS property. It will also destroy their Effects, cleaning up any active subscriptions. +[See more examples below.](#usage) -While hidden, children still re-render in response to new props, albeit at a lower priority than the rest of the content. +#### Modes {/*modes*/} -When the boundary becomes visible again, React will reveal the children with their previous state restored, and re-create their Effects. +An Activity boundary supports two modes: -In this way, Activity can be thought of as a mechanism for rendering "background activity". Rather than completely discarding content that's likely to become visible again, you can use Activity to maintain and restore that content's UI and internal state, while ensuring that your hidden content has no unwanted side effects. +- In `visible` mode, React renders the children and runs the setup functions for + their `useEffect` and `useLayoutEffect` calls. +- In `hidden` mode, React hides the children and runs the cleanup functions for + their `useEffect` and `useLayoutEffect` calls. React preserves their state and + renders updates at a lower priority than updates to visible content. -[See more examples below.](#usage) +When a hidden Activity boundary becomes visible, React reveals its children with +their previous state and runs their Effect setup functions again. + +In React DOM, hiding an Activity boundary applies `display: none` to the nearest +DOM elements inside the boundary. React preserves those elements while the +boundary remains mounted. #### Props {/*props*/} -* `children`: The UI you intend to show and hide. -* `mode`: A string value of either `'visible'` or `'hidden'`. If omitted, defaults to `'visible'`. +* `children`: The UI rendered by the Activity boundary. `children` can be any + [React node](/reference/react/isValidElement#react-elements-vs-react-nodes). +* **optional** `mode`: Either `'visible'` or `'hidden'`. Defaults to `'visible'`. + See [Modes](#modes) for the behavior of each value. +* **optional** `name`: A string that identifies the Activity boundary in React + Developer Tools. #### Caveats {/*caveats*/} -- If an Activity is rendered inside of a [ViewTransition](/reference/react/ViewTransition), and it becomes visible as a result of an update caused by [startTransition](/reference/react/startTransition), it will activate the ViewTransition's `enter` animation. If it becomes hidden, it will activate its `exit` animation. -- A *hidden* Activity that just renders text will not render anything rather than rendering hidden text, because there’s no corresponding DOM element to apply visibility changes to. For example, `` will not produce any output in the DOM for `const ComponentThatJustReturnsText = () => "Hello, World!"`. `` will render visible text. +- Hiding an Activity boundary retains its state and DOM nodes, so React does not + reclaim all memory associated with the hidden subtree. +- Browser behavior associated with preserved DOM nodes can continue while the + boundary is hidden. For example, audio and video can continue playing. Use an + Effect cleanup function to stop this behavior. + [See an example below.](#my-hidden-components-have-unwanted-side-effects) +- React runs cleanup functions for Effects created with `useEffect` and + `useLayoutEffect` when an Activity boundary becomes hidden. Insertion Effects + created with + [`useInsertionEffect`](/reference/react/useInsertionEffect) + remain connected because styles may still be needed by the preserved DOM. +- React detaches refs in a hidden Activity boundary and reattaches them when the + boundary becomes visible. +- Content initially rendered inside `` is not included in + server-rendered HTML. React renders it on the client at a lower priority after + hydrating visible content. +- React omits text-only output while an Activity boundary is hidden because a text + node cannot receive `display: none`. The text appears when the boundary becomes + visible. +- If an Activity boundary is inside + [``](/reference/react/ViewTransition), + changing it from hidden to visible as part of an update started with + [`startTransition`](/reference/react/startTransition) activates the `enter` + animation. Changing it from visible to hidden as part of that update activates + the `exit` animation. --- ## Usage {/*usage*/} -### Restoring the state of hidden components {/*restoring-the-state-of-hidden-components*/} - -In React, when you want to conditionally show or hide a component, you typically mount or unmount it based on that condition: - -```jsx -{isShowingSidebar && ( - -)} -``` - -But unmounting a component destroys its internal state, which is not always what you want. - -When you hide a component using an Activity boundary instead, React will "save" its state for later: - -```jsx - - - -``` - -This makes it possible to hide and then later restore components in the state they were previously in. - -The following example has a sidebar with an expandable section. You can press "Overview" to reveal the three subitems below it. The main app area also has a button that hides and shows the sidebar. - -Try expanding the Overview section, and then toggling the sidebar closed then open: - - - -```js src/App.js active -import { useState } from 'react'; -import Sidebar from './Sidebar.js'; +### Preserving state while content is hidden {/*restoring-the-state-of-hidden-components*/} -export default function App() { - const [isShowingSidebar, setIsShowingSidebar] = useState(true); +When this condition becomes false, React removes `` from the tree and +discards its state: - return ( - <> - {isShowingSidebar && ( - - )} - -
- -

Main content

-
- - ); -} -``` - -```js src/Sidebar.js -import { useState } from 'react'; - -export default function Sidebar() { - const [isExpanded, setIsExpanded] = useState(false) - - return ( - - ); -} -``` - -```css -body { height: 275px; margin: 0; } -#root { - display: flex; - gap: 10px; - height: 100%; -} -nav { - padding: 10px; - background: #eee; - font-size: 14px; - height: 100%; -} -main { - padding: 10px; -} -p { - margin: 0; -} -h1 { - margin-top: 10px; -} -.indicator { - margin-left: 4px; - display: inline-block; - rotate: 90deg; -} -.indicator.down { - rotate: 180deg; -} +```js +{isShowingSidebar && } ``` -
- -The Overview section always starts out collapsed. Because we unmount the sidebar when `isShowingSidebar` flips to `false`, all its internal state is lost. - -This is a perfect use case for Activity. We can preserve the internal state of our sidebar, even when visually hiding it. - -Let's replace the conditional rendering of our sidebar with an Activity boundary: +Render the component inside an Activity boundary to preserve its state while it is +hidden: -```jsx {7,9} -// Before -{isShowingSidebar && ( - -)} - -// After +```js ``` -and check out the new behavior: +In this example, expand the sidebar, hide it, and show it again. The expanded state +is preserved. -```js src/App.js active +```js import { Activity, useState } from 'react'; -import Sidebar from './Sidebar.js'; - export default function App() { const [isShowingSidebar, setIsShowingSidebar] = useState(true); return ( - <> - +
+
-

Main content

- +
); } -``` -```js src/Sidebar.js -import { useState } from 'react'; - -export default function Sidebar() { - const [isExpanded, setIsExpanded] = useState(false) +function Sidebar() { + const [isExpanded, setIsExpanded] = useState(false); return ( -