<Activity mode="hidden"> hides a subtree without unmounting it. React 7,897 applies display: none to the children and tears down their effects — subscriptions, timers and sockets stop — but keeps their DOM and their state. Switch the mode back to visible and the state is where the user left it, effects re-created. Hidden children still re-render on new props, below the priority of anything visible.
import { Activity, useState } from 'react';
export default function Workspace() {
const [tab, setTab] = useState('search');
return <>
<button onClick={() => setTab('search')}>Search</button>
<button onClick={() => setTab('map')}>Map</button>
<Activity mode={tab === 'search' ? 'visible' : 'hidden'}><SearchPanel /></Activity>
<Activity mode={tab === 'map' ? 'visible' : 'hidden'}><MapPanel /></Activity>
</>;
}Written as {tab === 'map' && <MapPanel />}, switching tabs throws away the scroll position, the zoom level and every input in the panel. Hidden with a CSS class instead, the state survives but the effects keep running: the map keeps polling, the socket stays open. Activity is the combination you want, and the supported way to pre-render a screen the user is likely to open next.
Two caveats. A hidden Activity whose child renders only text produces no DOM output, because there is no element to hide. And DOM-level side effects survive hiding: a playing <video> keeps playing unless a cleanup pauses it. <Activity> is stable as of React 19.2, released 1 October 2025.