[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"article-react-weekly-activity-component":44,"search-suggestions":445,"path-membership-react-weekly-activity-component":484,"related-react-weekly-activity-component":485,"code:jsx:true:1zqed8":603,"code:jsx:true:9y4s21":604,"code:jsx:true:km64tc":605,"code:jsx:true:cieiew":606,"code:jsx:true:1sc5x9n":607,"code:jsx:true:sd7xp1":608,"comments-01a1623b-51e7-7669-89ad-eb54d67696eb":609},[4,20,32],{"id":5,"slug":6,"name":7,"tagline":8,"description":9,"accentFrom":10,"accentTo":11,"icon":12,"defaultLocale":13,"locales":14,"features":16,"position":19},"019fe637-3d33-714b-b57f-23e163ffca0c","dev","Web Development","Read it. Run it. Prove it.","A post a day on modern web development — most with an editable playground and a quiz that explains every answer. Free, no account needed.","violet-500","cyan-400","◇","en",[13,15],"fa",{"courses":17,"paths":17,"articles":17,"exams":18,"flashcards":18,"packages":17,"community":17,"certificates":17,"teams":17,"commerce":17},true,false,0,{"id":21,"slug":22,"name":23,"tagline":24,"description":25,"accentFrom":26,"accentTo":10,"icon":27,"defaultLocale":13,"locales":28,"features":30,"position":31},"019fe637-3dc2-754c-8657-0f175bfee7c6","lang","Languages","Learn a language the way you learn a codebase.","Grammar explained the way good documentation explains an API — one idea at a time, each with a quiz.","amber-400","⌘",[13,15,29],"es",{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":17,"packages":18,"community":17,"certificates":17,"teams":18,"commerce":18},2,{"id":33,"slug":34,"name":35,"tagline":36,"description":37,"accentFrom":38,"accentTo":39,"icon":40,"defaultLocale":13,"locales":41,"features":42,"position":43},"7b3c16f2-931d-410e-802e-e1fa4edab7de","soft","Soft Skills","The half of the job nobody wrote documentation for.","Weekly, on the parts of working life that decide more than your code does — first weeks, meetings, interviews, promotions, and the people around you. Written from what actually happens, and recorded as a podcast you can listen to on the walk.","emerald-400","teal-300","◉",[13],{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":18,"packages":18,"community":17,"certificates":18,"teams":18,"commerce":18},3,{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":53,"likeCount":19,"commentCount":19,"author":54,"vertical":59,"topic":60,"tags":63,"_count":74,"playground":76,"body":78,"bodyMd":395,"seo":396,"translationGroupId":398,"series":399,"podcastUrl":48,"verticalId":5,"thread":434,"assessments":436,"translations":441,"quiz":443},"01a1623b-51e7-7669-89ad-eb54d67696eb","react-weekly-activity-component","React Activity Component: The State Between Render and Remount",null,"React's Activity component hides a subtree without unmounting it — what state, effects, and renders actually do, plus a copy-paste cheat sheet.","\u002Fmedia\u002Fcovers\u002Freact-weekly-activity-component.png",14,"2026-11-01T18:40:36.479Z",25,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},"react","React",[64,65,68,71],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},"javascript","Javascript",{"slug":69,"name":70,"color":48},"tutorial","Tutorial",{"slug":72,"name":73,"color":48},"webdev","Webdev",{"assessments":75},1,{"slug":46,"title":77},"React &lt;Activity&gt; — interactive playground",{"blocks":79,"version":75},[80,84,87,90,95,98,107,110,113,116,127,131,134,140,143,146,150,153,156,159,162,196,199,202,206,209,212,215,218,222,225,228,232,235,238,241,246,249,252,255,258,266,269,277,280,284,287,291,294,298,301,305,308,312,315,318,322,325,332,335,338,341,344,347,350,353,356,359,362,365,368,371,377,380,383,386,389],{"id":81,"html":82,"type":83},"b1","\u003Cp>You hide a settings panel with \u003Ccode>{isOpen &amp;&amp; &lt;Panel \u002F&gt;}\u003C\u002Fcode>. The user reopens it two seconds later, and the unsaved toggle they&#39;d flipped is back to its default. The scroll position is gone. The debounced search they&#39;d typed into a nested filter has vanished. Nothing is broken — React unmounted \u003Ccode>&lt;Panel \u002F&gt;\u003C\u002Fcode> and mounted a brand new one, exactly as that code tells it to. You just didn&#39;t mean to ask for that.\u003C\u002Fp>","paragraph",{"id":85,"html":86,"type":83},"b2","\u003Cp>Before React 19.2, there was no built-in way to say &quot;hide this, but keep it exactly as it was.&quot; CSS (\u003Ccode>display: none\u003C\u002Fcode> while the component stays mounted) keeps the state, but its effects keep running in the background — timers ticking, subscriptions open, polling a screen nobody can see. Unmounting runs cleanup correctly but throws the state away. There was no option between &quot;alive and visible&quot; and &quot;gone.&quot;\u003C\u002Fp>",{"id":88,"html":89,"type":83},"b3","\u003Cp>\u003Cstrong>React 19.2\u003C\u002Fstrong> (released October 2025) added \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> to close that gap: a built-in way to hide a subtree while keeping its state intact. \u003Cstrong>React 19.3\u003C\u002Fstrong> — released September 2026 and patched to the current npm \u003Ccode>latest\u003C\u002Fcode>, \u003Cstrong>19.3.1\u003C\u002Fstrong>, in October — shipped several bug fixes for it, including one around \u003Ccode>useSyncExternalStore\u003C\u002Fcode> updates while hidden, that are worth understanding on their own, because they show exactly where the &quot;hidden but alive&quot; model gets subtle. This is episode eight of \u003Cstrong>React Deep Dive\u003C\u002Fstrong>, and it picks up where \u003Ca href=\"https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Freact-re-render-vs-remount-what-actually-triggers-each-5fok\">episode one, on re-render vs. remount\u003C\u002Fa>, left off: \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> is neither of those two things. It&#39;s a third state, with its own rules, and treating it like either of the other two is exactly how the 19.3 bugs happened.\u003C\u002Fp>",{"id":91,"html":92,"text":93,"type":94,"level":31},"b4","What you&#39;ll learn","What you'll learn","heading",{"id":96,"html":97,"type":83},"b5","\u003Cp>By the end of this article you&#39;ll be able to:\u003C\u002Fp>",{"id":99,"type":100,"items":101,"ordered":18},"b6","list",[102,103,104,105,106],"Explain precisely what \u003Ccode>&lt;Activity mode=&quot;hidden&quot;&gt;\u003C\u002Fcode> preserves and what it tears down — state, effects, refs, and renders are four separate questions, not one","Replace a \u003Ccode>{condition &amp;&amp; &lt;Panel\u002F&gt;}\u003C\u002Fcode> pattern with \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> when hiding, not destroying, is what you actually want","Predict when a hidden subtree still re-renders (and why that&#39;s not a bug)","Avoid the two traps that the 19.3 patch notes had to fix for the React team itself","Use \u003Ccode>&lt;StrictMode&gt;\u003C\u002Fcode> to catch the effect-cleanup bugs that \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> surfaces",{"id":108,"html":109,"text":109,"type":94,"level":31},"b7","Who this is for",{"id":111,"html":112,"type":83},"b8","\u003Cp>You&#39;re comfortable with function components, \u003Ccode>useState\u003C\u002Fcode>, and \u003Ccode>useEffect\u003C\u002Fcode>, and you&#39;ve been bitten at least once by state resetting when you only meant to hide something. You don&#39;t need to have used \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> before.\u003C\u002Fp>",{"id":114,"html":115,"text":115,"type":94,"level":31},"b9","Table of contents",{"id":117,"type":100,"items":118,"ordered":18},"b10",[119,120,121,122,123,124,125,126],"\u003Ca href=\"#the-problem-hiding-isnt-unmounting-and-unmounting-isnt-hiding\">The problem: hiding isn&#39;t unmounting, and unmounting isn&#39;t hiding\u003C\u002Fa>","\u003Ca href=\"#the-mental-model\">The mental model\u003C\u002Fa>","\u003Ca href=\"#the-walkthrough\">The walkthrough\u003C\u002Fa>","\u003Ca href=\"#edge-cases-and-gotchas\">Edge cases and gotchas\u003C\u002Fa>","\u003Ca href=\"#best-practices\">Best practices\u003C\u002Fa>","\u003Ca href=\"#faq\">FAQ\u003C\u002Fa>","\u003Ca href=\"#cheat-sheet\">Cheat sheet\u003C\u002Fa>","\u003Ca href=\"#key-takeaways\">Key takeaways\u003C\u002Fa>",{"id":128,"html":129,"text":130,"type":94,"level":31},"b11","The problem: hiding isn&#39;t unmounting, and unmounting isn&#39;t hiding","The problem: hiding isn't unmounting, and unmounting isn't hiding",{"id":132,"html":133,"type":83},"b12","\u003Cp>Say you&#39;re building a dashboard with three report tabs. Each tab has its own view: a filter form, a live-updating counter from a \u003Ccode>useEffect\u003C\u002Fcode>-driven interval (think &quot;records processed,&quot; polled every second), and a draft note the user is typing. Switch tabs, and here&#39;s the naive version:\u003C\u002Fp>",{"id":135,"code":136,"type":137,"language":138,"highlight":139},"b13","function Dashboard({ activeTab }) {\n  return (\n    \u003C>\n      {activeTab === \"sales\" && \u003CReportPanel label=\"Sales\" \u002F>}\n      {activeTab === \"ops\" && \u003CReportPanel label=\"Ops\" \u002F>}\n      {activeTab === \"support\" && \u003CReportPanel label=\"Support\" \u002F>}\n    \u003C\u002F>\n  );\n}","code","jsx",[],{"id":141,"html":142,"type":83},"b14","\u003Cp>This works, in the sense that it compiles and nothing throws. But switch from &quot;Sales&quot; to &quot;Ops&quot; and back, and the Sales \u003Ccode>ReportPanel\u003C\u002Fcode> is a \u003Cstrong>completely new instance\u003C\u002Fstrong>. Its counter restarts at zero. Its draft note is empty. Its \u003Ccode>useEffect\u003C\u002Fcode> cleanup ran when you left, and a fresh mount effect ran when you came back — including any expensive setup it does on mount. You wanted a tab switch; React gave you a demolition and a rebuild.\u003C\u002Fp>",{"id":144,"html":145,"type":83},"b15","\u003Cp>The instinctive fix is to keep everything mounted and hide the inactive panels with CSS:\u003C\u002Fp>",{"id":147,"code":148,"type":137,"language":138,"highlight":149},"b16","function Dashboard({ activeTab }) {\n  return (\n    \u003C>\n      \u003Cdiv style={{ display: activeTab === \"sales\" ? \"block\" : \"none\" }}>\n        \u003CReportPanel label=\"Sales\" \u002F>\n      \u003C\u002Fdiv>\n      \u003Cdiv style={{ display: activeTab === \"ops\" ? \"block\" : \"none\" }}>\n        \u003CReportPanel label=\"Ops\" \u002F>\n      \u003C\u002Fdiv>\n      \u003Cdiv style={{ display: activeTab === \"support\" ? \"block\" : \"none\" }}>\n        \u003CReportPanel label=\"Support\" \u002F>\n      \u003C\u002Fdiv>\n    \u003C\u002F>\n  );\n}",[],{"id":151,"html":152,"type":83},"b17","\u003Cp>This keeps the state, but now you&#39;ve traded one bug for another: the Sales panel&#39;s interval keeps polling, its effects keep subscribing, and its hidden DOM keeps costing you layout and memory for every tab the user isn&#39;t looking at. You&#39;ve solved the state problem by un-solving the cleanup problem.\u003C\u002Fp>",{"id":154,"html":155,"type":83},"b18","\u003Cp>\u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> is React&#39;s answer to &quot;I want both&quot;: state survives, but effects still tear down and rebuild the way they would for a component you actually cared about keeping correct.\u003C\u002Fp>",{"id":157,"html":158,"text":158,"type":94,"level":31},"b19","The mental model",{"id":160,"html":161,"type":83},"b20","\u003Cp>\u003Cstrong>The mental model:\u003C\u002Fstrong> Render, remount, and \u003Ccode>&lt;Activity mode=&quot;hidden&quot;&gt;\u003C\u002Fcode> are three different operations, and they disagree on three separate things — \u003Cstrong>state\u003C\u002Fstrong>, \u003Cstrong>effects\u003C\u002Fstrong>, and \u003Cstrong>rendering\u003C\u002Fstrong> — not just one axis you can call &quot;aliveness.&quot;\u003C\u002Fp>",{"id":163,"head":164,"rows":169,"type":195},"b21",[165,166,167,168],"","Stays mounted (same instance)","\u003Ccode>&lt;Activity mode=&quot;hidden&quot;&gt;\u003C\u002Fcode>","Unmount (remove from the tree)",[170,175,180,185,190],[171,172,173,174],"\u003Cstrong>State\u003C\u002Fstrong> (\u003Ccode>useState\u003C\u002Fcode>, \u003Ccode>useReducer\u003C\u002Fcode>)","Preserved","\u003Cstrong>Preserved\u003C\u002Fstrong>","Destroyed",[176,177,178,179],"\u003Cstrong>Effects\u003C\u002Fstrong> (\u003Ccode>useEffect\u003C\u002Fcode>)","Keep running","\u003Cstrong>Torn down on hide, re-created on show\u003C\u002Fstrong>","Torn down, never re-created (new instance builds fresh ones)",[181,182,183,184],"\u003Cstrong>Rendering on prop\u002Fcontext change\u003C\u002Fstrong>","Happens normally","\u003Cstrong>Still happens, at lower priority\u003C\u002Fstrong>","Doesn&#39;t happen — there&#39;s nothing there",[186,187,188,189],"\u003Cstrong>DOM\u003C\u002Fstrong>","Visible","Hidden (\u003Ccode>display: none\u003C\u002Fcode>), node kept","Removed",[191,192,193,194],"\u003Cstrong>Refs\u003C\u002Fstrong>","Point at the live node","Still point at the (now hidden) node","Become \u003Ccode>null\u003C\u002Fcode>","table",{"id":197,"html":198,"type":83},"b22","\u003Cp>Read the middle column again: hidden is not the same as paused, and it is not the same as destroyed. A component inside a hidden \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> keeps its state, loses its effects, and — this is the part people get wrong — \u003Cstrong>keeps rendering\u003C\u002Fstrong> when something above it changes, just at a priority React considers less urgent than whatever&#39;s actually on screen. That last row is the one the 19.3 patch notes had to fix bugs around, because it&#39;s the one that breaks the intuition &quot;hidden means nothing happens here.&quot;\u003C\u002Fp>",{"id":200,"html":201,"type":83},"b23","\u003Cp>Here&#39;s the shape in code, next to the two things it&#39;s between:\u003C\u002Fp>",{"id":203,"code":204,"type":137,"language":138,"highlight":205},"b24","import { Activity, useState } from \"react\";\n\nfunction Dashboard({ activeTab }) {\n  return (\n    \u003C>\n      \u003CActivity mode={activeTab === \"sales\" ? \"visible\" : \"hidden\"}>\n        \u003CReportPanel label=\"Sales\" \u002F>\n      \u003C\u002FActivity>\n      \u003CActivity mode={activeTab === \"ops\" ? \"visible\" : \"hidden\"}>\n        \u003CReportPanel label=\"Ops\" \u002F>\n      \u003C\u002FActivity>\n      \u003CActivity mode={activeTab === \"support\" ? \"visible\" : \"hidden\"}>\n        \u003CReportPanel label=\"Support\" \u002F>\n      \u003C\u002FActivity>\n    \u003C\u002F>\n  );\n}",[],{"id":207,"html":208,"type":83},"b25","\u003Cp>Switch tabs now, and the inactive \u003Ccode>ReportPanel\u003C\u002Fcode>s stay mounted as far as state is concerned (the draft note survives), but their effects clean up (the polling interval stops) the moment they go hidden, and start fresh again the moment they&#39;re shown. No wasted polling, no lost draft.\u003C\u002Fp>",{"id":210,"html":211,"type":83},"b26","\u003Cp>\u003Cstrong>Historical aside, labelled:\u003C\u002Fstrong> before 19.2 stabilized this, an experimental build exposed the same idea as \u003Ccode>unstable_Activity\u003C\u002Fcode>. If you see \u003Ccode>import { unstable_Activity as Activity } from &quot;react&quot;\u003C\u002Fcode> in an older blog post or a pinned pre-19.2 dependency, that&#39;s the pre-stable name — don&#39;t write it in new code. The stable export is just \u003Ccode>Activity\u003C\u002Fcode>, no prefix.\u003C\u002Fp>",{"id":213,"html":214,"text":214,"type":94,"level":31},"b27","The walkthrough",{"id":216,"html":217,"text":217,"type":94,"level":43},"b28","Step 1 — the smallest correct example",{"id":219,"code":220,"type":137,"language":138,"highlight":221},"b29","import { Activity, useState } from \"react\";\n\nfunction Sidebar({ isOpen }) {\n  const [filter, setFilter] = useState(\"\");\n\n  return (\n    \u003CActivity mode={isOpen ? \"visible\" : \"hidden\"}>\n      \u003Cinput value={filter} onChange={(e) => setFilter(e.target.value)} \u002F>\n    \u003C\u002FActivity>\n  );\n}",[],{"id":223,"html":224,"type":83},"b30","\u003Cp>\u003Cstrong>Key concept:\u003C\u002Fstrong> \u003Ccode>mode\u003C\u002Fcode> is the entire API surface for visibility — \u003Ccode>&quot;visible&quot;\u003C\u002Fcode> or \u003Ccode>&quot;hidden&quot;\u003C\u002Fcode> (it defaults to \u003Ccode>&quot;visible&quot;\u003C\u002Fcode> if you omit it). There&#39;s no \u003Ccode>mode=&quot;unmounted&quot;\u003C\u002Fcode>; if you want a true unmount, don&#39;t render the \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> at all. \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> only ever answers &quot;show or hide,&quot; never &quot;destroy.&quot;\u003C\u002Fp>",{"id":226,"html":227,"text":227,"type":94,"level":43},"b31","Step 2 — proving the effect actually tears down",{"id":229,"code":230,"type":137,"language":138,"highlight":231},"b32","function ReportPanel({ label }) {\n  const [count, setCount] = useState(0);\n  const [draft, setDraft] = useState(\"\");\n\n  useEffect(() => {\n    const id = setInterval(() => setCount((c) => c + 1), 1000);\n    return () => clearInterval(id); \u002F\u002F runs when Activity hides this subtree\n  }, []);\n\n  return (\n    \u003Cdiv>\n      \u003Ch3>{label}: {count}\u003C\u002Fh3>\n      \u003Ctextarea value={draft} onChange={(e) => setDraft(e.target.value)} \u002F>\n    \u003C\u002Fdiv>\n  );\n}",[],{"id":233,"html":234,"type":83},"b33","\u003Cp>Hide this panel&#39;s \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> and the interval&#39;s cleanup runs immediately — \u003Ccode>count\u003C\u002Fcode> stops climbing. Show it again, and the effect&#39;s setup function runs again, starting a new interval. \u003Ccode>count\u003C\u002Fcode> and \u003Ccode>draft\u003C\u002Fcode> were never touched by any of this; they&#39;re still sitting in React&#39;s fiber for this component, exactly where you left them. The playground below is this exact component, running the real reconciler, so you can watch the counter freeze and the draft survive in real time.\u003C\u002Fp>",{"id":236,"html":237,"text":237,"type":94,"level":43},"b34","Step 3 — the row people miss: hidden subtrees still render",{"id":239,"html":240,"type":83},"b35","\u003Cp>This is the one the mental model table flags and most tutorials skip. A hidden \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> doesn&#39;t freeze its children — it keeps re-rendering them when their props or context change, just scheduled at a lower priority than the visible UI. Why would React bother? Two real reasons, both from the official docs:\u003C\u002Fp>",{"id":242,"type":100,"items":243,"ordered":18},"b36",[244,245],"\u003Cstrong>Pre-rendering.\u003C\u002Fstrong> A \u003Ccode>&lt;Activity mode=&quot;hidden&quot;&gt;\u003C\u002Fcode> on \u003Cem>initial\u003C\u002Fem> render still renders its children (without mounting their effects), which lets data-fetching components start loading before the user ever opens that tab — so when they do, it&#39;s instant instead of a fresh Suspense fallback.","\u003Cstrong>Keeping derived UI correct.\u003C\u002Fstrong> If a hidden panel&#39;s rendered output depends on a prop from its parent (say, a shared &quot;last synced at&quot; timestamp), it should show the right value the instant it&#39;s revealed, not a stale one from the moment it was hidden.",{"id":247,"html":248,"type":83},"b37","\u003Cp>The playground&#39;s &quot;Bump shared prop&quot; button demonstrates this directly: it changes a value passed into every \u003Ccode>ReportPanel\u003C\u002Fcode>, visible or hidden, and the hidden panel&#39;s render count still climbs — while its effect-mount count does not. Those two counters moving independently \u003Cem>is\u003C\u002Fem> the mental model, observed instead of described.\u003C\u002Fp>",{"id":250,"html":251,"text":251,"type":94,"level":43},"b38","Step 4 — what the 19.3 bug fixes actually tell you",{"id":253,"html":254,"type":83},"b39","\u003Cp>React 19.3&#39;s changelog lists several \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> fixes: issues with \u003Ccode>useSyncExternalStore\u003C\u002Fcode> updates while hidden, portal visibility, metadata hoisting, and error handling. The pattern across them is instructive: every one is a place where &quot;still rendering, but hidden, with effects torn down&quot; created a seam that a feature built on a simpler always-mounted\u002Fnever-mounted assumption fell through. \u003Ccode>useSyncExternalStore\u003C\u002Fcode> subscribes to an external store through an effect-like mechanism — exactly what \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> tears down on hide — so an update arriving while hidden needs special handling to avoid being silently lost or double-applied on reveal. If you subscribe to anything external (WebSockets, browser APIs, a state manager outside React) inside a component that might live under an \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode>, assume the subscription is gone while hidden and gets rebuilt from current state when shown.\u003C\u002Fp>",{"id":256,"html":257,"text":257,"type":94,"level":31},"b40","Edge cases and gotchas",{"id":259,"type":100,"items":260,"ordered":18},"b41",[261,262,263,264,265],"\u003Cstrong>Text-only children render nothing when hidden.\u003C\u002Fstrong> Hiding works by applying \u003Ccode>display: &quot;none&quot;\u003C\u002Fcode> to a wrapper — bare text has no element to attach that to, so it just doesn&#39;t render. Wrap bare text in an element if it might end up inside an \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode>.","\u003Cstrong>Side effects outside \u003Ccode>useEffect\u003C\u002Fcode> keep running.\u003C\u002Fstrong> A \u003Ccode>&lt;video&gt;\u003C\u002Fcode>, \u003Ccode>&lt;audio&gt;\u003C\u002Fcode>, or \u003Ccode>&lt;iframe&gt;\u003C\u002Fcode> isn&#39;t paused by hiding — its DOM node is still alive, still doing whatever it was doing. Pause it explicitly, in a \u003Ccode>useLayoutEffect\u003C\u002Fcode> cleanup tied to the visual hide.","\u003Cstrong>Refs still point at hidden DOM.\u003C\u002Fstrong> Unmounting nulls a ref; hiding doesn&#39;t — the node still exists, just invisible. Code that treats &quot;ref is set&quot; as &quot;user can see it&quot; will be wrong.","\u003Cstrong>StrictMode is how you catch the bugs this exposes.\u003C\u002Fstrong> Effects now mount and unmount far more often than a typical lifetime suggests — every hide\u002Fshow cycle, not just mount\u002Funmount. Any effect that assumed its cleanup never really had to undo everything gets exposed fast. Develop with \u003Ccode>&lt;StrictMode&gt;\u003C\u002Fcode> so this surfaces before production does.","\u003Cstrong>Interaction with \u003Ccode>&lt;ViewTransition&gt;\u003C\u002Fcode>.\u003C\u002Fstrong> If an \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> reveal happens inside \u003Ccode>startTransition\u003C\u002Fcode>, and the tree uses \u003Ca href=\"https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Freact-193-viewtransition-animate-state-without-losing-it-4kpp\">\u003Ccode>&lt;ViewTransition&gt;\u003C\u002Fcode>\u003C\u002Fa> (also stable in 19.3), showing it triggers that boundary&#39;s \u003Ccode>enter\u003C\u002Fcode> animation and hiding triggers \u003Ccode>exit\u003C\u002Fcode> — the two features compose by design.",{"id":267,"html":268,"text":268,"type":94,"level":31},"b42","Best practices",{"id":270,"type":100,"items":271,"ordered":18},"b43",[272,273,274,275,276],"\u003Cstrong>Reach for \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> when state is expensive to rebuild and the hide is temporary\u003C\u002Fstrong> — tabs, a collapsible sidebar, a wizard step, a modal reopened with the same draft. The signal: the user will plausibly come back to this exact state.","\u003Cstrong>Don&#39;t make it a default replacement for conditional rendering.\u003C\u002Fstrong> If the user is done with something for good (a dismissed notification, a finished onboarding step), unmounting is correct and cheaper — nothing left to render at low priority, nothing to reconcile later.","\u003Cstrong>Don&#39;t treat it as a pause button for real-world side effects\u003C\u002Fstrong> (payments in flight, a write to a server). Hiding a subtree doesn&#39;t cancel an in-flight promise; it only affects effect lifecycle and state.","\u003Cstrong>If a hidden subtree subscribes to anything external\u003C\u002Fstrong>, write its effect assuming the subscription is gone while hidden — resync from current state on the next mount rather than assuming buffered delivery.","\u003Cstrong>Keep \u003Ccode>mode\u003C\u002Fcode> a \u003Ccode>useState\u003C\u002Fcode>\u002Fprop-driven value\u003C\u002Fstrong>, not something you mutate imperatively — \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> reacts to re-renders like anything else in React.",{"id":278,"html":279,"text":279,"type":94,"level":31},"b44","FAQ",{"id":281,"html":282,"text":283,"type":94,"level":43},"b45","Is \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> the same thing as \u003Ccode>display: none\u003C\u002Fcode>?","Is \u003CActivity> the same thing as display: none?",{"id":285,"html":286,"type":83},"b46","\u003Cp>No. It uses \u003Ccode>display: none\u003C\u002Fcode> for the visual hiding, but that&#39;s one of four things it does — it also tears down effects (plain CSS never does), preserves state across the hide (an unmount never does), and keeps rendering hidden children at lower priority (a component you&#39;ve stopped rendering can&#39;t do at all).\u003C\u002Fp>",{"id":288,"html":289,"text":290,"type":94,"level":43},"b47","Does \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> replace \u003Ccode>key\u003C\u002Fcode>-based remounting?","Does \u003CActivity> replace key-based remounting?",{"id":292,"html":293,"type":83},"b48","\u003Cp>No — opposite problems. \u003Ccode>key\u003C\u002Fcode> forces React to treat something as a \u003Cem>new\u003C\u002Fem> instance, which is right when you want fresh state. \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> keeps the \u003Cem>same\u003C\u002Fem> instance alive while hidden. Want a reset? Use \u003Ccode>key\u003C\u002Fcode>. Want to preserve exactly what&#39;s there? Use \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode>.\u003C\u002Fp>",{"id":295,"html":296,"text":297,"type":94,"level":43},"b49","Will \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> fetch data for a hidden tab before the user opens it?","Will \u003CActivity> fetch data for a hidden tab before the user opens it?",{"id":299,"html":300,"type":83},"b50","\u003Cp>Under specific conditions: if the hidden children read data through something Suspense-activating — a promise read with \u003Ccode>use()\u003C\u002Fcode> — a hidden \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> can pre-render and start that fetch early. Data fetched inside a \u003Ccode>useEffect\u003C\u002Fcode> is not: effects don&#39;t run while hidden, so effect-based fetching only starts once the subtree becomes visible.\u003C\u002Fp>",{"id":302,"html":303,"text":304,"type":94,"level":43},"b51","Does hiding with \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> cost anything if I never show the content again?","Does hiding with \u003CActivity> cost anything if I never show the content again?",{"id":306,"html":307,"type":83},"b52","\u003Cp>Some — the hidden subtree still occupies a spot in the fiber tree and can still re-render at low priority. For content the user is realistically done with, unmount it; reach for \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> specifically because you expect a return visit.\u003C\u002Fp>",{"id":309,"html":310,"text":311,"type":94,"level":43},"b53","Can I nest \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> components?","Can I nest \u003CActivity> components?",{"id":313,"html":314,"type":83},"b54","\u003Cp>Yes — visibility composes as expected. A \u003Ccode>visible\u003C\u002Fcode> \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> inside a \u003Ccode>hidden\u003C\u002Fcode> one is still effectively hidden, because its ancestor is hidden.\u003C\u002Fp>",{"id":316,"html":317,"text":317,"type":94,"level":31},"b55","Cheat sheet",{"id":319,"code":320,"type":137,"language":138,"highlight":321},"b56","import { Activity, useState, useEffect } from \"react\";\n\n\u002F\u002F 1. Import — stable since React 19.2, no prefix.\n\u002F\u002F import { unstable_Activity as Activity } from \"react\"; \u002F\u002F ❌ pre-19.2 only\n\n\u002F\u002F 2. Usage — mode is the whole API.\n\u003CActivity mode={isVisible ? \"visible\" : \"hidden\"}>\n  \u003CExpensiveSubtree \u002F>\n\u003C\u002FActivity>\n\n\u002F\u002F 3. What survives hiding vs. what doesn't:\n\u002F\u002F    state (useState\u002FuseReducer)         → preserved\n\u002F\u002F    effects (useEffect)                 → torn down on hide, re-created on show\n\u002F\u002F    rendering on prop\u002Fcontext change    → still happens, lower priority\n\u002F\u002F    DOM node                            → kept, display: none\n\u002F\u002F    refs                                → still point at the (hidden) node\n\n\u002F\u002F 4. Pick the right tool:\n\u002F\u002F    \"destroy it for good\"        → don't render it (plain conditional)\n\u002F\u002F    \"reset it on purpose\"        → key={newId}\n\u002F\u002F    \"hide it, keep it alive\"     → \u003CActivity mode=\"hidden\">\n\n\u002F\u002F 5. Gotchas:\n\u002F\u002F    - bare text child while hidden renders nothing (no element to hide)\n\u002F\u002F    - \u003Cvideo>\u002F\u003Caudio>\u002F\u003Ciframe> keep running while hidden — pause explicitly\n\u002F\u002F      in a useLayoutEffect cleanup if that matters\n\u002F\u002F    - external subscriptions (useSyncExternalStore, sockets) are torn down\n\u002F\u002F      on hide — resync on remount, don't assume buffered delivery\n\u002F\u002F    - develop with \u003CStrictMode> to catch effects that assumed they'd only\n\u002F\u002F      ever run once",[],{"id":323,"html":324,"text":324,"type":94,"level":31},"b57","Key takeaways",{"id":326,"type":100,"items":327,"ordered":18},"b58",[328,329,330,331],"\u003Ccode>&lt;Activity mode=&quot;hidden&quot;&gt;\u003C\u002Fcode> is a third state, not a point on the line between &quot;mounted&quot; and &quot;unmounted&quot;: state is preserved, effects are torn down and rebuilt, and rendering on prop change still happens — just at lower priority.","It solves the specific problem of keeping expensive-to-rebuild state alive across a hide\u002Fshow cycle without paying the cost of a fully-live, always-rendering component.","The 19.3 bug fixes for \u003Ccode>useSyncExternalStore\u003C\u002Fcode>, portals, and metadata under \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> all trace back to the same seam: features built assuming &quot;mounted&quot; or &quot;unmounted&quot; as the only two states had to be taught about the third one.","Reach for it when the user will plausibly come back; reach for \u003Ccode>key\u003C\u002Fcode> when you want a clean reset; reach for plain conditional rendering when the content is genuinely done.",{"id":333,"html":334,"type":83},"b59","\u003Cp>That chat-draft bug from episode one and the dashboard tab bug here are mirror images of each other — one was an accidental remount nobody wanted, this one is an accidental unmount nobody wanted. Same underlying lesson both times: React only does what your JSX actually asks for, never what you meant. Have you reached for \u003Ccode>&lt;Activity&gt;\u003C\u002Fcode> yet, or are you still hiding things with CSS and hoping the effects behave? Tell me what you&#39;re using it for in the comments.\u003C\u002Fp>",{"id":336,"html":337,"type":83},"b60","\u003C!-- playground:start -->",{"id":339,"html":340,"text":340,"type":94,"level":31},"b61","🎮 Try it yourself",{"id":342,"html":343,"type":83},"b62","\u003Cp>\u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-activity-component\u002Fplayground\">▶️ Open the interactive playground →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":345,"html":346,"type":83},"b63","\u003Cp>\u003Cem>Runs right in your browser — poke at it and watch the concept react live.\u003C\u002Fem>\u003C\u002Fp>",{"id":348,"html":349,"type":83},"b64","\u003C!-- playground:end -->",{"id":351,"html":352,"type":83},"b65","\u003C!-- quiz:start -->",{"id":354,"html":355,"text":355,"type":94,"level":31},"b66","🧠 Test yourself",{"id":357,"html":358,"type":83},"b67","\u003Cp>Think it clicked? \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-activity-component\u002Fquiz\">Take the 8-question quiz →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":360,"html":361,"type":83},"b68","\u003Cp>\u003Cem>Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.\u003C\u002Fem>\u003C\u002Fp>",{"id":363,"html":364,"type":83},"b69","\u003C!-- quiz:end -->",{"id":366,"html":367,"type":83},"b70","\u003C!-- related:start -->",{"id":369,"html":370,"text":370,"type":94,"level":31},"b71","📚 Read next",{"id":372,"type":100,"items":373,"ordered":18},"b72",[374,375,376],"\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-viewtransition-component\">React 19.3 ViewTransition: Animate State Without Losing It\u003C\u002Fa>","\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-derived-state-bug\">React Derived State: Why That useState Is Probably a Bug\u003C\u002Fa>","\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-rerender-vs-remount\">React Re-render vs Remount: What Actually Triggers Each\u003C\u002Fa>",{"id":378,"html":379,"type":83},"b73","\u003C!-- related:end -->",{"id":381,"type":382},"b74","divider",{"id":384,"html":385,"type":83},"b75","\u003Cp>🚀 \u003Cstrong>Want more like this?\u003C\u002Fstrong> Every guide, playground, and quiz lives on \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">bestpractic.org\u003C\u002Fa>\u003C\u002Fstrong> — open it and \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">sign up free\u003C\u002Fa>\u003C\u002Fstrong> so the next one finds you.\u003C\u002Fp>",{"id":387,"html":388,"type":83},"b76","\u003Cp>\u003Cem>Thanks for reading! Let&#39;s stay connected:\u003C\u002Fem>\u003C\u002Fp>",{"id":390,"type":100,"items":391,"ordered":18},"b77",[392,393,394],"⭐ \u003Cstrong>GitHub\u003C\u002Fstrong> — follow me and star the projects: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fparsajiravand\">github.com\u002Fparsajiravand\u003C\u002Fa>","💬 \u003Cstrong>Discord\u003C\u002Fstrong> — join the frontend best-practices community: \u003Ca href=\"https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ\">discord.gg\u002Fd9KRhuAwQ\u003C\u002Fa>","📸 \u003Cstrong>Instagram\u003C\u002Fstrong> — frontend best practices, daily: \u003Ca href=\"https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F\">@bestpractice___\u003C\u002Fa>","You hide a settings panel with `{isOpen && \u003CPanel \u002F>}`. The user reopens it two seconds later, and the unsaved toggle they'd flipped is back to its default. The scroll position is gone. The debounced search they'd typed into a nested filter has vanished. Nothing is broken — React unmounted `\u003CPanel \u002F>` and mounted a brand new one, exactly as that code tells it to. You just didn't mean to ask for that.\n\nBefore React 19.2, there was no built-in way to say \"hide this, but keep it exactly as it was.\" CSS (`display: none` while the component stays mounted) keeps the state, but its effects keep running in the background — timers ticking, subscriptions open, polling a screen nobody can see. Unmounting runs cleanup correctly but throws the state away. There was no option between \"alive and visible\" and \"gone.\"\n\n**React 19.2** (released October 2025) added `\u003CActivity>` to close that gap: a built-in way to hide a subtree while keeping its state intact. **React 19.3** — released September 2026 and patched to the current npm `latest`, **19.3.1**, in October — shipped several bug fixes for it, including one around `useSyncExternalStore` updates while hidden, that are worth understanding on their own, because they show exactly where the \"hidden but alive\" model gets subtle. This is episode eight of **React Deep Dive**, and it picks up where [episode one, on re-render vs. remount](https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Freact-re-render-vs-remount-what-actually-triggers-each-5fok), left off: `\u003CActivity>` is neither of those two things. It's a third state, with its own rules, and treating it like either of the other two is exactly how the 19.3 bugs happened.\n\n## What you'll learn\n\nBy the end of this article you'll be able to:\n\n- Explain precisely what `\u003CActivity mode=\"hidden\">` preserves and what it tears down — state, effects, refs, and renders are four separate questions, not one\n- Replace a `{condition && \u003CPanel\u002F>}` pattern with `\u003CActivity>` when hiding, not destroying, is what you actually want\n- Predict when a hidden subtree still re-renders (and why that's not a bug)\n- Avoid the two traps that the 19.3 patch notes had to fix for the React team itself\n- Use `\u003CStrictMode>` to catch the effect-cleanup bugs that `\u003CActivity>` surfaces\n\n## Who this is for\n\nYou're comfortable with function components, `useState`, and `useEffect`, and you've been bitten at least once by state resetting when you only meant to hide something. You don't need to have used `\u003CActivity>` before.\n\n## Table of contents\n\n- [The problem: hiding isn't unmounting, and unmounting isn't hiding](#the-problem-hiding-isnt-unmounting-and-unmounting-isnt-hiding)\n- [The mental model](#the-mental-model)\n- [The walkthrough](#the-walkthrough)\n- [Edge cases and gotchas](#edge-cases-and-gotchas)\n- [Best practices](#best-practices)\n- [FAQ](#faq)\n- [Cheat sheet](#cheat-sheet)\n- [Key takeaways](#key-takeaways)\n\n## The problem: hiding isn't unmounting, and unmounting isn't hiding\n\nSay you're building a dashboard with three report tabs. Each tab has its own view: a filter form, a live-updating counter from a `useEffect`-driven interval (think \"records processed,\" polled every second), and a draft note the user is typing. Switch tabs, and here's the naive version:\n\n```jsx\nfunction Dashboard({ activeTab }) {\n  return (\n    \u003C>\n      {activeTab === \"sales\" && \u003CReportPanel label=\"Sales\" \u002F>}\n      {activeTab === \"ops\" && \u003CReportPanel label=\"Ops\" \u002F>}\n      {activeTab === \"support\" && \u003CReportPanel label=\"Support\" \u002F>}\n    \u003C\u002F>\n  );\n}\n```\n\nThis works, in the sense that it compiles and nothing throws. But switch from \"Sales\" to \"Ops\" and back, and the Sales `ReportPanel` is a **completely new instance**. Its counter restarts at zero. Its draft note is empty. Its `useEffect` cleanup ran when you left, and a fresh mount effect ran when you came back — including any expensive setup it does on mount. You wanted a tab switch; React gave you a demolition and a rebuild.\n\nThe instinctive fix is to keep everything mounted and hide the inactive panels with CSS:\n\n```jsx\nfunction Dashboard({ activeTab }) {\n  return (\n    \u003C>\n      \u003Cdiv style={{ display: activeTab === \"sales\" ? \"block\" : \"none\" }}>\n        \u003CReportPanel label=\"Sales\" \u002F>\n      \u003C\u002Fdiv>\n      \u003Cdiv style={{ display: activeTab === \"ops\" ? \"block\" : \"none\" }}>\n        \u003CReportPanel label=\"Ops\" \u002F>\n      \u003C\u002Fdiv>\n      \u003Cdiv style={{ display: activeTab === \"support\" ? \"block\" : \"none\" }}>\n        \u003CReportPanel label=\"Support\" \u002F>\n      \u003C\u002Fdiv>\n    \u003C\u002F>\n  );\n}\n```\n\nThis keeps the state, but now you've traded one bug for another: the Sales panel's interval keeps polling, its effects keep subscribing, and its hidden DOM keeps costing you layout and memory for every tab the user isn't looking at. You've solved the state problem by un-solving the cleanup problem.\n\n`\u003CActivity>` is React's answer to \"I want both\": state survives, but effects still tear down and rebuild the way they would for a component you actually cared about keeping correct.\n\n## The mental model\n\n**The mental model:** Render, remount, and `\u003CActivity mode=\"hidden\">` are three different operations, and they disagree on three separate things — **state**, **effects**, and **rendering** — not just one axis you can call \"aliveness.\"\n\n| | Stays mounted (same instance) | `\u003CActivity mode=\"hidden\">` | Unmount (remove from the tree) |\n| --- | --- | --- | --- |\n| **State** (`useState`, `useReducer`) | Preserved | **Preserved** | Destroyed |\n| **Effects** (`useEffect`) | Keep running | **Torn down on hide, re-created on show** | Torn down, never re-created (new instance builds fresh ones) |\n| **Rendering on prop\u002Fcontext change** | Happens normally | **Still happens, at lower priority** | Doesn't happen — there's nothing there |\n| **DOM** | Visible | Hidden (`display: none`), node kept | Removed |\n| **Refs** | Point at the live node | Still point at the (now hidden) node | Become `null` |\n\nRead the middle column again: hidden is not the same as paused, and it is not the same as destroyed. A component inside a hidden `\u003CActivity>` keeps its state, loses its effects, and — this is the part people get wrong — **keeps rendering** when something above it changes, just at a priority React considers less urgent than whatever's actually on screen. That last row is the one the 19.3 patch notes had to fix bugs around, because it's the one that breaks the intuition \"hidden means nothing happens here.\"\n\nHere's the shape in code, next to the two things it's between:\n\n```jsx\nimport { Activity, useState } from \"react\";\n\nfunction Dashboard({ activeTab }) {\n  return (\n    \u003C>\n      \u003CActivity mode={activeTab === \"sales\" ? \"visible\" : \"hidden\"}>\n        \u003CReportPanel label=\"Sales\" \u002F>\n      \u003C\u002FActivity>\n      \u003CActivity mode={activeTab === \"ops\" ? \"visible\" : \"hidden\"}>\n        \u003CReportPanel label=\"Ops\" \u002F>\n      \u003C\u002FActivity>\n      \u003CActivity mode={activeTab === \"support\" ? \"visible\" : \"hidden\"}>\n        \u003CReportPanel label=\"Support\" \u002F>\n      \u003C\u002FActivity>\n    \u003C\u002F>\n  );\n}\n```\n\nSwitch tabs now, and the inactive `ReportPanel`s stay mounted as far as state is concerned (the draft note survives), but their effects clean up (the polling interval stops) the moment they go hidden, and start fresh again the moment they're shown. No wasted polling, no lost draft.\n\n**Historical aside, labelled:** before 19.2 stabilized this, an experimental build exposed the same idea as `unstable_Activity`. If you see `import { unstable_Activity as Activity } from \"react\"` in an older blog post or a pinned pre-19.2 dependency, that's the pre-stable name — don't write it in new code. The stable export is just `Activity`, no prefix.\n\n## The walkthrough\n\n### Step 1 — the smallest correct example\n\n```jsx\nimport { Activity, useState } from \"react\";\n\nfunction Sidebar({ isOpen }) {\n  const [filter, setFilter] = useState(\"\");\n\n  return (\n    \u003CActivity mode={isOpen ? \"visible\" : \"hidden\"}>\n      \u003Cinput value={filter} onChange={(e) => setFilter(e.target.value)} \u002F>\n    \u003C\u002FActivity>\n  );\n}\n```\n\n**Key concept:** `mode` is the entire API surface for visibility — `\"visible\"` or `\"hidden\"` (it defaults to `\"visible\"` if you omit it). There's no `mode=\"unmounted\"`; if you want a true unmount, don't render the `\u003CActivity>` at all. `\u003CActivity>` only ever answers \"show or hide,\" never \"destroy.\"\n\n### Step 2 — proving the effect actually tears down\n\n```jsx\nfunction ReportPanel({ label }) {\n  const [count, setCount] = useState(0);\n  const [draft, setDraft] = useState(\"\");\n\n  useEffect(() => {\n    const id = setInterval(() => setCount((c) => c + 1), 1000);\n    return () => clearInterval(id); \u002F\u002F runs when Activity hides this subtree\n  }, []);\n\n  return (\n    \u003Cdiv>\n      \u003Ch3>{label}: {count}\u003C\u002Fh3>\n      \u003Ctextarea value={draft} onChange={(e) => setDraft(e.target.value)} \u002F>\n    \u003C\u002Fdiv>\n  );\n}\n```\n\nHide this panel's `\u003CActivity>` and the interval's cleanup runs immediately — `count` stops climbing. Show it again, and the effect's setup function runs again, starting a new interval. `count` and `draft` were never touched by any of this; they're still sitting in React's fiber for this component, exactly where you left them. The playground below is this exact component, running the real reconciler, so you can watch the counter freeze and the draft survive in real time.\n\n### Step 3 — the row people miss: hidden subtrees still render\n\nThis is the one the mental model table flags and most tutorials skip. A hidden `\u003CActivity>` doesn't freeze its children — it keeps re-rendering them when their props or context change, just scheduled at a lower priority than the visible UI. Why would React bother? Two real reasons, both from the official docs:\n\n- **Pre-rendering.** A `\u003CActivity mode=\"hidden\">` on *initial* render still renders its children (without mounting their effects), which lets data-fetching components start loading before the user ever opens that tab — so when they do, it's instant instead of a fresh Suspense fallback.\n- **Keeping derived UI correct.** If a hidden panel's rendered output depends on a prop from its parent (say, a shared \"last synced at\" timestamp), it should show the right value the instant it's revealed, not a stale one from the moment it was hidden.\n\nThe playground's \"Bump shared prop\" button demonstrates this directly: it changes a value passed into every `ReportPanel`, visible or hidden, and the hidden panel's render count still climbs — while its effect-mount count does not. Those two counters moving independently *is* the mental model, observed instead of described.\n\n### Step 4 — what the 19.3 bug fixes actually tell you\n\nReact 19.3's changelog lists several `\u003CActivity>` fixes: issues with `useSyncExternalStore` updates while hidden, portal visibility, metadata hoisting, and error handling. The pattern across them is instructive: every one is a place where \"still rendering, but hidden, with effects torn down\" created a seam that a feature built on a simpler always-mounted\u002Fnever-mounted assumption fell through. `useSyncExternalStore` subscribes to an external store through an effect-like mechanism — exactly what `\u003CActivity>` tears down on hide — so an update arriving while hidden needs special handling to avoid being silently lost or double-applied on reveal. If you subscribe to anything external (WebSockets, browser APIs, a state manager outside React) inside a component that might live under an `\u003CActivity>`, assume the subscription is gone while hidden and gets rebuilt from current state when shown.\n\n## Edge cases and gotchas\n\n- **Text-only children render nothing when hidden.** Hiding works by applying `display: \"none\"` to a wrapper — bare text has no element to attach that to, so it just doesn't render. Wrap bare text in an element if it might end up inside an `\u003CActivity>`.\n- **Side effects outside `useEffect` keep running.** A `\u003Cvideo>`, `\u003Caudio>`, or `\u003Ciframe>` isn't paused by hiding — its DOM node is still alive, still doing whatever it was doing. Pause it explicitly, in a `useLayoutEffect` cleanup tied to the visual hide.\n- **Refs still point at hidden DOM.** Unmounting nulls a ref; hiding doesn't — the node still exists, just invisible. Code that treats \"ref is set\" as \"user can see it\" will be wrong.\n- **StrictMode is how you catch the bugs this exposes.** Effects now mount and unmount far more often than a typical lifetime suggests — every hide\u002Fshow cycle, not just mount\u002Funmount. Any effect that assumed its cleanup never really had to undo everything gets exposed fast. Develop with `\u003CStrictMode>` so this surfaces before production does.\n- **Interaction with `\u003CViewTransition>`.** If an `\u003CActivity>` reveal happens inside `startTransition`, and the tree uses [`\u003CViewTransition>`](https:\u002F\u002Fdev.to\u002Fparsajiravand\u002Freact-193-viewtransition-animate-state-without-losing-it-4kpp) (also stable in 19.3), showing it triggers that boundary's `enter` animation and hiding triggers `exit` — the two features compose by design.\n\n## Best practices\n\n- **Reach for `\u003CActivity>` when state is expensive to rebuild and the hide is temporary** — tabs, a collapsible sidebar, a wizard step, a modal reopened with the same draft. The signal: the user will plausibly come back to this exact state.\n- **Don't make it a default replacement for conditional rendering.** If the user is done with something for good (a dismissed notification, a finished onboarding step), unmounting is correct and cheaper — nothing left to render at low priority, nothing to reconcile later.\n- **Don't treat it as a pause button for real-world side effects** (payments in flight, a write to a server). Hiding a subtree doesn't cancel an in-flight promise; it only affects effect lifecycle and state.\n- **If a hidden subtree subscribes to anything external**, write its effect assuming the subscription is gone while hidden — resync from current state on the next mount rather than assuming buffered delivery.\n- **Keep `mode` a `useState`\u002Fprop-driven value**, not something you mutate imperatively — `\u003CActivity>` reacts to re-renders like anything else in React.\n\n## FAQ\n\n### Is `\u003CActivity>` the same thing as `display: none`?\n\nNo. It uses `display: none` for the visual hiding, but that's one of four things it does — it also tears down effects (plain CSS never does), preserves state across the hide (an unmount never does), and keeps rendering hidden children at lower priority (a component you've stopped rendering can't do at all).\n\n### Does `\u003CActivity>` replace `key`-based remounting?\n\nNo — opposite problems. `key` forces React to treat something as a *new* instance, which is right when you want fresh state. `\u003CActivity>` keeps the *same* instance alive while hidden. Want a reset? Use `key`. Want to preserve exactly what's there? Use `\u003CActivity>`.\n\n### Will `\u003CActivity>` fetch data for a hidden tab before the user opens it?\n\nUnder specific conditions: if the hidden children read data through something Suspense-activating — a promise read with `use()` — a hidden `\u003CActivity>` can pre-render and start that fetch early. Data fetched inside a `useEffect` is not: effects don't run while hidden, so effect-based fetching only starts once the subtree becomes visible.\n\n### Does hiding with `\u003CActivity>` cost anything if I never show the content again?\n\nSome — the hidden subtree still occupies a spot in the fiber tree and can still re-render at low priority. For content the user is realistically done with, unmount it; reach for `\u003CActivity>` specifically because you expect a return visit.\n\n### Can I nest `\u003CActivity>` components?\n\nYes — visibility composes as expected. A `visible` `\u003CActivity>` inside a `hidden` one is still effectively hidden, because its ancestor is hidden.\n\n## Cheat sheet\n\n```jsx\nimport { Activity, useState, useEffect } from \"react\";\n\n\u002F\u002F 1. Import — stable since React 19.2, no prefix.\n\u002F\u002F import { unstable_Activity as Activity } from \"react\"; \u002F\u002F ❌ pre-19.2 only\n\n\u002F\u002F 2. Usage — mode is the whole API.\n\u003CActivity mode={isVisible ? \"visible\" : \"hidden\"}>\n  \u003CExpensiveSubtree \u002F>\n\u003C\u002FActivity>\n\n\u002F\u002F 3. What survives hiding vs. what doesn't:\n\u002F\u002F    state (useState\u002FuseReducer)         → preserved\n\u002F\u002F    effects (useEffect)                 → torn down on hide, re-created on show\n\u002F\u002F    rendering on prop\u002Fcontext change    → still happens, lower priority\n\u002F\u002F    DOM node                            → kept, display: none\n\u002F\u002F    refs                                → still point at the (hidden) node\n\n\u002F\u002F 4. Pick the right tool:\n\u002F\u002F    \"destroy it for good\"        → don't render it (plain conditional)\n\u002F\u002F    \"reset it on purpose\"        → key={newId}\n\u002F\u002F    \"hide it, keep it alive\"     → \u003CActivity mode=\"hidden\">\n\n\u002F\u002F 5. Gotchas:\n\u002F\u002F    - bare text child while hidden renders nothing (no element to hide)\n\u002F\u002F    - \u003Cvideo>\u002F\u003Caudio>\u002F\u003Ciframe> keep running while hidden — pause explicitly\n\u002F\u002F      in a useLayoutEffect cleanup if that matters\n\u002F\u002F    - external subscriptions (useSyncExternalStore, sockets) are torn down\n\u002F\u002F      on hide — resync on remount, don't assume buffered delivery\n\u002F\u002F    - develop with \u003CStrictMode> to catch effects that assumed they'd only\n\u002F\u002F      ever run once\n```\n\n## Key takeaways\n\n- `\u003CActivity mode=\"hidden\">` is a third state, not a point on the line between \"mounted\" and \"unmounted\": state is preserved, effects are torn down and rebuilt, and rendering on prop change still happens — just at lower priority.\n- It solves the specific problem of keeping expensive-to-rebuild state alive across a hide\u002Fshow cycle without paying the cost of a fully-live, always-rendering component.\n- The 19.3 bug fixes for `useSyncExternalStore`, portals, and metadata under `\u003CActivity>` all trace back to the same seam: features built assuming \"mounted\" or \"unmounted\" as the only two states had to be taught about the third one.\n- Reach for it when the user will plausibly come back; reach for `key` when you want a clean reset; reach for plain conditional rendering when the content is genuinely done.\n\nThat chat-draft bug from episode one and the dashboard tab bug here are mirror images of each other — one was an accidental remount nobody wanted, this one is an accidental unmount nobody wanted. Same underlying lesson both times: React only does what your JSX actually asks for, never what you meant. Have you reached for `\u003CActivity>` yet, or are you still hiding things with CSS and hoping the effects behave? Tell me what you're using it for in the comments.\n\n\u003C!-- playground:start -->\n\n## 🎮 Try it yourself\n\n**[▶️ Open the interactive playground →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-activity-component\u002Fplayground)**\n\n_Runs right in your browser — poke at it and watch the concept react live._\n\n\u003C!-- playground:end -->\n\n\u003C!-- quiz:start -->\n\n## 🧠 Test yourself\n\nThink it clicked? **[Take the 8-question quiz →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-activity-component\u002Fquiz)**\n\n_Instant feedback, a hint on every question, and an explanation for each answer — right or wrong._\n\n\u003C!-- quiz:end -->\n\n\u003C!-- related:start -->\n\n## 📚 Read next\n\n- [React 19.3 ViewTransition: Animate State Without Losing It](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-viewtransition-component)\n- [React Derived State: Why That useState Is Probably a Bug](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-derived-state-bug)\n- [React Re-render vs Remount: What Actually Triggers Each](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-rerender-vs-remount)\n\n\u003C!-- related:end -->\n\n---\n\n🚀 **Want more like this?** Every guide, playground, and quiz lives on **[bestpractic.org](https:\u002F\u002Fbestpractic.org\u002F)** — open it and **[sign up free](https:\u002F\u002Fbestpractic.org\u002F)** so the next one finds you.\n\n*Thanks for reading! Let's stay connected:*\n\n- ⭐ **GitHub** — follow me and star the projects: [github.com\u002Fparsajiravand](https:\u002F\u002Fgithub.com\u002Fparsajiravand)\n- 💬 **Discord** — join the frontend best-practices community: [discord.gg\u002Fd9KRhuAwQ](https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ)\n- 📸 **Instagram** — frontend best practices, daily: [@bestpractice___](https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F)",{"title":47,"canonical":397,"description":49},"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Freact-weekly-activity-component","01a1623b-51e7-7669-89ad-ed6931fd8003",{"name":400,"part":401,"total":401,"items":402},"React Deep Dive",8,[403,408,412,416,420,425,429,433],{"slug":404,"title":405,"publishedAt":406,"readingMinutes":407},"react-weekly-rerender-vs-remount","React Re-render vs Remount: What Actually Triggers Each","2026-08-29T12:32:41.200Z",13,{"slug":409,"title":410,"publishedAt":411,"readingMinutes":407},"react-weekly-compiler-memoization","React Compiler 1.0: What useMemo You Can Delete","2026-09-05T10:17:49.849Z",{"slug":413,"title":414,"publishedAt":415,"readingMinutes":407},"react-weekly-form-actions-pending-state","React Form Actions: useActionState & useFormStatus Guide","2026-09-12T10:23:52.928Z",{"slug":417,"title":418,"publishedAt":419,"readingMinutes":51},"react-weekly-derived-state-bug","React Derived State: Why That useState Is Probably a Bug","2026-09-20T16:27:07.271Z",{"slug":421,"title":422,"publishedAt":423,"readingMinutes":424},"react-weekly-viewtransition-component","React 19.3 ViewTransition: Animate State Without Losing It","2026-10-03T11:22:26.437Z",15,{"slug":426,"title":427,"publishedAt":428,"readingMinutes":407},"react-weekly-fragment-refs","React 19.3 Fragment Refs: Skip the Wrapper Div for a Ref","2026-10-10T12:12:38.989Z",{"slug":430,"title":431,"publishedAt":432,"readingMinutes":424},"react-weekly-useeffectevent-stale-closures","React 19.2 useEffectEvent: Why It Won't Fix Your Deps Array","2026-10-17T12:20:57.469Z",{"slug":46,"title":47,"publishedAt":52,"readingMinutes":51},{"id":435,"locked":18},"01a1623b-5228-779f-9d92-a8de7fbb12af",[437],{"id":438,"slug":46,"title":439,"_count":440},"01a1623b-5253-717a-970f-511d5f004bdd","React Activity Component — test yourself",{"questions":401},[442],{"locale":13,"slug":46},{"id":438,"slug":46,"title":439,"_count":444,"questionCount":401},{"questions":401},[446,448,450,454,456,460,464,468,470,474,477,480],{"slug":72,"name":73,"articles":447},162,{"slug":66,"name":67,"articles":449},131,{"slug":451,"name":452,"articles":453},"frontend","Frontend",92,{"slug":69,"name":70,"articles":455},64,{"slug":457,"name":458,"articles":459},"css","Css",49,{"slug":461,"name":462,"articles":463},"performance","Performance",27,{"slug":465,"name":466,"articles":467},"typescript","Typescript",23,{"slug":61,"name":62,"articles":469},20,{"slug":471,"name":472,"articles":473},"programming","Programming",16,{"slug":475,"name":476,"articles":473},"browser","Browser",{"slug":478,"name":479,"articles":51},"node","Node",{"slug":481,"name":482,"articles":483},"html","Html",11,[],{"items":486,"meta":601},[487,499,518,533,553,568,583],{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":488,"likeCount":19,"commentCount":19,"author":489,"vertical":490,"topic":491,"tags":492,"_count":497,"playground":498,"hasQuiz":17,"hasPlayground":17},26,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[493,494,495,496],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":46},{"id":500,"slug":501,"title":502,"subtitle":48,"excerpt":503,"coverUrl":504,"locale":13,"readingMinutes":505,"publishedAt":506,"viewCount":507,"likeCount":19,"commentCount":19,"author":508,"vertical":509,"topic":510,"tags":511,"_count":516,"playground":517,"hasQuiz":17,"hasPlayground":17},"01a14e81-eaa5-700a-a6f6-3be4ffe47c2b","usesyncexternalstore-tearing-fix","Your Custom Hooks Tear in React 18. `useSyncExternalStore` Fixes It.","A useState + useEffect hook that subscribes to window size, localStorage, or a WebSocket looks fine until React 18's concurrent rendering shows two parts of your UI two different values at once. useSyncExternalStore is the hook built to stop that.","\u002Fmedia\u002Fcovers\u002Fusesyncexternalstore-tearing-fix.png",5,"2026-10-23T06:11:03.306Z",245,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[512,513,514,515],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},{"slug":72,"name":73,"color":48},{"slug":451,"name":452,"color":48},{"assessments":75},{"slug":501},{"id":519,"slug":430,"title":431,"subtitle":48,"excerpt":520,"coverUrl":521,"locale":13,"readingMinutes":424,"publishedAt":432,"viewCount":522,"likeCount":19,"commentCount":19,"author":523,"vertical":524,"topic":525,"tags":526,"_count":531,"playground":532,"hasQuiz":17,"hasPlayground":17},"01a13e2e-d5ae-707f-a6c5-726f2ed01ab0","React's useEffectEvent (stable in 19.2) kills stale closures in Effects without a ref trick — but it's not a deps-array shortcut. Guide + cheat sheet.","\u002Fmedia\u002Fcovers\u002Freact-weekly-useeffectevent-stale-closures.png",233,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[527,528,529,530],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":430},{"id":534,"slug":535,"title":536,"subtitle":48,"excerpt":537,"coverUrl":538,"locale":13,"readingMinutes":424,"publishedAt":539,"viewCount":540,"likeCount":19,"commentCount":19,"author":541,"vertical":542,"topic":543,"tags":546,"_count":551,"playground":552,"hasQuiz":17,"hasPlayground":17},"01a120b9-d611-74ef-ad15-a267677c3251","nextjs-weekly-image-priority-quality-changes","Next.js 16 Image: priority Deprecated, quality Now Allowlisted","Next.js 16 quietly deprecated Image's priority for preload and restricts quality to an allowlist (default: 75). What breaks, plus a copy-paste cheat sheet.","\u002Fmedia\u002Fcovers\u002Fnextjs-weekly-image-priority-quality-changes.png","2026-10-13T06:10:29.815Z",543,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":544,"name":545},"nextjs","Nextjs",[547,548,549,550],{"slug":544,"name":545,"color":48},{"slug":61,"name":62,"color":48},{"slug":72,"name":73,"color":48},{"slug":69,"name":70,"color":48},{"assessments":75},{"slug":535},{"id":554,"slug":426,"title":427,"subtitle":48,"excerpt":555,"coverUrl":556,"locale":13,"readingMinutes":407,"publishedAt":428,"viewCount":557,"likeCount":19,"commentCount":19,"author":558,"vertical":559,"topic":560,"tags":561,"_count":566,"playground":567,"hasQuiz":17,"hasPlayground":17},"01a117e0-acb2-740f-9391-db919729cf21","React 19.3 Fragment Refs let you ref a sibling group with no wrapper div. Learn the FragmentInstance API, its limits, and get a copy-paste cheat sheet.","\u002Fmedia\u002Fcovers\u002Freact-weekly-fragment-refs.png",481,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[562,563,564,565],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":426},{"id":569,"slug":421,"title":422,"subtitle":48,"excerpt":570,"coverUrl":571,"locale":13,"readingMinutes":424,"publishedAt":423,"viewCount":572,"likeCount":19,"commentCount":19,"author":573,"vertical":574,"topic":575,"tags":576,"_count":581,"playground":582,"hasQuiz":17,"hasPlayground":17},"01a0f615-d2be-750b-839e-02fd75291b4e","React 19.3's ViewTransition component animates state changes, not just page swaps — the mental model, enter\u002Fexit\u002Fupdate\u002Fshare, and a cheat sheet.","\u002Fmedia\u002Fcovers\u002Freact-weekly-viewtransition-component.png",545,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[577,578,579,580],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},{"slug":69,"name":70,"color":48},{"slug":72,"name":73,"color":48},{"assessments":75},{"slug":421},{"id":584,"slug":585,"title":586,"subtitle":48,"excerpt":587,"coverUrl":588,"locale":13,"readingMinutes":505,"publishedAt":589,"viewCount":590,"likeCount":19,"commentCount":19,"author":591,"vertical":592,"topic":593,"tags":594,"_count":599,"playground":600,"hasQuiz":17,"hasPlayground":17},"01a0ec73-247c-7738-84ae-80c9d1d116a8","input-lag-bug-debounce-cant-fix","Your Search Box Lags. Debounce Hides It; `useTransition` Fixes It","A big list re-render was blocking every keystroke — debouncing just moved the freeze. useTransition tells React which update can wait.","\u002Fmedia\u002Fcovers\u002Finput-lag-bug-debounce-cant-fix.png","2026-10-03T11:21:55.564Z",399,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[595,596,597,598],{"slug":61,"name":62,"color":48},{"slug":66,"name":67,"color":48},{"slug":72,"name":73,"color":48},{"slug":461,"name":462,"color":48},{"assessments":75},{"slug":585},{"page":75,"perPage":602,"total":469,"totalPages":43},7,"\u003Cdiv class=\"shj shj-lang-jsx shj-multiline\" data-lang=\"jsx\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Dashboard\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> activeTab \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-oper\">{\u003C\u002Fspan>activeTab \u003Cspan class=\"shj-oper\">===\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"sales\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">&#38;&#38;\u003C\u002Fspan> \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">ReportPanel\u003C\u002Fspan> \u003Cspan class=\"shj-class\">label\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"Sales\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\n      \u003Cspan class=\"shj-oper\">{\u003C\u002Fspan>activeTab \u003Cspan class=\"shj-oper\">===\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"ops\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">&#38;&#38;\u003C\u002Fspan> \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">ReportPanel\u003C\u002Fspan> \u003Cspan class=\"shj-class\">label\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"Ops\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\n      \u003Cspan class=\"shj-oper\">{\u003C\u002Fspan>activeTab \u003Cspan class=\"shj-oper\">===\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"support\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">&#38;&#38;\u003C\u002Fspan> \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">ReportPanel\u003C\u002Fspan> \u003Cspan class=\"shj-class\">label\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"Support\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-jsx shj-multiline\" data-lang=\"jsx\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003Cdiv>15\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Dashboard\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> activeTab \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">div\u003C\u002Fspan> \u003Cspan class=\"shj-class\">style\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> display\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> activeTab \u003Cspan class=\"shj-oper\">===\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"sales\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">?\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"block\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"none\"\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n        \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">ReportPanel\u003C\u002Fspan> \u003Cspan class=\"shj-class\">label\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"Sales\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">div\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">div\u003C\u002Fspan> \u003Cspan class=\"shj-class\">style\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> display\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> activeTab \u003Cspan class=\"shj-oper\">===\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"ops\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">?\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"block\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"none\"\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n        \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">ReportPanel\u003C\u002Fspan> \u003Cspan class=\"shj-class\">label\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"Ops\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">div\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">div\u003C\u002Fspan> \u003Cspan class=\"shj-class\">style\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> display\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> activeTab \u003Cspan class=\"shj-oper\">===\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"support\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">?\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"block\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"none\"\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n        \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">ReportPanel\u003C\u002Fspan> \u003Cspan class=\"shj-class\">label\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"Support\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">div\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-jsx shj-multiline\" data-lang=\"jsx\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003Cdiv>15\u003C\u002Fdiv>\u003Cdiv>16\u003C\u002Fdiv>\u003Cdiv>17\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Activity\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> useState \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"react\"\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Dashboard\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> activeTab \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">Activity\u003C\u002Fspan> \u003Cspan class=\"shj-class\">mode\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>activeTab \u003Cspan class=\"shj-oper\">===\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"sales\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">?\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"visible\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"hidden\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n        \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">ReportPanel\u003C\u002Fspan> \u003Cspan class=\"shj-class\">label\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"Sales\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">Activity\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">Activity\u003C\u002Fspan> \u003Cspan class=\"shj-class\">mode\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>activeTab \u003Cspan class=\"shj-oper\">===\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"ops\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">?\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"visible\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"hidden\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n        \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">ReportPanel\u003C\u002Fspan> \u003Cspan class=\"shj-class\">label\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"Ops\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">Activity\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">Activity\u003C\u002Fspan> \u003Cspan class=\"shj-class\">mode\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>activeTab \u003Cspan class=\"shj-oper\">===\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"support\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">?\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"visible\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"hidden\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n        \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">ReportPanel\u003C\u002Fspan> \u003Cspan class=\"shj-class\">label\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"Support\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">Activity\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-jsx shj-multiline\" data-lang=\"jsx\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Activity\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> useState \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"react\"\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Sidebar\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> isOpen \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>filter\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> setFilter\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">useState\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">Activity\u003C\u002Fspan> \u003Cspan class=\"shj-class\">mode\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>isOpen \u003Cspan class=\"shj-oper\">?\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"visible\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"hidden\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">input\u003C\u002Fspan> \u003Cspan class=\"shj-class\">value\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>filter\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan> \u003Cspan class=\"shj-class\">onChange\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>e\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-func\">setFilter\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>e\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>target\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>value\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">Activity\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-jsx shj-multiline\" data-lang=\"jsx\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003Cdiv>15\u003C\u002Fdiv>\u003Cdiv>16\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-class\">ReportPanel\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> label \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>count\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> setCount\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">useState\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">0\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>draft\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> setDraft\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">useState\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\n  \u003Cspan class=\"shj-func\">useEffect\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> id \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-func\">setInterval\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-func\">setCount\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>c\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> c \u003Cspan class=\"shj-oper\">+\u003C\u002Fspan> \u003Cspan class=\"shj-num\">1\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-num\">1000\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n    \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-func\">clearInterval\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>id\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F runs when Activity hides this subtree\n\u003C\u002Fspan>  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">div\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">h3\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">{\u003C\u002Fspan>label\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>: \u003Cspan class=\"shj-oper\">{\u003C\u002Fspan>count\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">h3\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">textarea\u003C\u002Fspan> \u003Cspan class=\"shj-class\">value\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>draft\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan> \u003Cspan class=\"shj-class\">onChange\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>e\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-func\">setDraft\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>e\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>target\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>value\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\n    \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">div\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-jsx shj-multiline\" data-lang=\"jsx\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003Cdiv>15\u003C\u002Fdiv>\u003Cdiv>16\u003C\u002Fdiv>\u003Cdiv>17\u003C\u002Fdiv>\u003Cdiv>18\u003C\u002Fdiv>\u003Cdiv>19\u003C\u002Fdiv>\u003Cdiv>20\u003C\u002Fdiv>\u003Cdiv>21\u003C\u002Fdiv>\u003Cdiv>22\u003C\u002Fdiv>\u003Cdiv>23\u003C\u002Fdiv>\u003Cdiv>24\u003C\u002Fdiv>\u003Cdiv>25\u003C\u002Fdiv>\u003Cdiv>26\u003C\u002Fdiv>\u003Cdiv>27\u003C\u002Fdiv>\u003Cdiv>28\u003C\u002Fdiv>\u003Cdiv>29\u003C\u002Fdiv>\u003Cdiv>30\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">import\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Activity\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> useState\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> useEffect \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"react\"\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F 1. Import — stable since React 19.2, no prefix.\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F import { unstable_Activity as Activity } from \"react\"; \u002F\u002F ❌ pre-19.2 only\n\u003C\u002Fspan>\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F 2. Usage — mode is the whole API.\n\u003C\u002Fspan>\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">Activity\u003C\u002Fspan> \u003Cspan class=\"shj-class\">mode\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">={\u003C\u002Fspan>isVisible \u003Cspan class=\"shj-oper\">?\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"visible\"\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"hidden\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-var\">ExpensiveSubtree\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F&gt;\u003C\u002Fspan>\n\u003Cspan class=\"shj-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-var\">Activity\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F 3. What survives hiding vs. what doesn't:\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F    state (useState\u002FuseReducer)         → preserved\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F    effects (useEffect)                 → torn down on hide, re-created on show\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F    rendering on prop\u002Fcontext change    → still happens, lower priority\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F    DOM node                            → kept, display: none\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F    refs                                → still point at the (hidden) node\n\u003C\u002Fspan>\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F 4. Pick the right tool:\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F    \"destroy it for good\"        → don't render it (plain conditional)\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F    \"reset it on purpose\"        → key={newId}\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F    \"hide it, keep it alive\"     → &lt;Activity mode=\"hidden\"&gt;\n\u003C\u002Fspan>\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F 5. Gotchas:\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F    - bare text child while hidden renders nothing (no element to hide)\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F    - &lt;video&gt;\u002F&lt;audio&gt;\u002F&lt;iframe&gt; keep running while hidden — pause explicitly\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F      in a useLayoutEffect cleanup if that matters\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F    - external subscriptions (useSyncExternalStore, sockets) are torn down\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F      on hide — resync on remount, don't assume buffered delivery\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F    - develop with &lt;StrictMode&gt; to catch effects that assumed they'd only\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F      ever run once\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>",{"locked":18,"total":19,"comments":610},[]]