Activity

<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.

Two panels that keep their state while hiddenJavaScript
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.