This article is published in English.
React 19.2 Explained: Activity, useEffectEvent, and Static Rendering
Learn how React 19.2's new Activity component, useEffectEvent hook, and partial static rendering fix hidden performance costs in modern UIs.
React 19.2 quietly acknowledges something developers have suspected for years: destroying and rebuilding components on every hide-and-show cycle was never truly cheap.
Picture a common scenario: a user flips between two tabs in your app. The list fetches its data again. The form clears itself out. Scroll position resets to the top. This happens on every single toggle, because React has been tearing down and rebuilding UI it didn't actually need to discard.
This isn't something you broke in your own code. It's simply how React has behaved since its earliest versions. With React 19.2, the framework finally addresses this head-on.
The Problem Nobody Talks About
Most performance advice circles around the same suspects: effects firing too often, redundant network requests, or forgetting to wrap a value in useMemo. Those concerns are legitimate. But there's a quieter cost that rarely gets mentioned: the overhead of mounting and unmounting components repeatedly.
Think about a tabbed dashboard, a mobile-style navigation stack, or a modal that gets opened and closed repeatedly during a single session. Every time you hide something using a conditional expression like {isVisible && <Component />}, React doesn't pause that piece of UI — it throws it away entirely. State is wiped. Effects tear down and fire again from scratch. When the content reappears, the DOM has to be reconstructed as if it never existed.
For small, simple components, this cost is invisible. But for data-heavy dashboards, embedded video players, or anything wrapping a heavyweight third-party widget, this repeated destruction and reconstruction becomes a real performance drain.
Solving exactly this problem was a central goal of React 19.2, alongside two other improvements developers have been asking for.
1. The Component — Pause before You Destroy
This is the flagship addition in the release, and it represents a genuinely new direction for how React handles hidden UI.
You can now mark a section of your interface as visible or hidden without ever fully unmounting it, using the new Activity component. When content is hidden, it keeps its existing state, skips running its effects, and its rendering work is deprioritized so it never competes with whatever is actually visible on screen.
import { unstable_Activity as Activity } from 'react';
function Dashboard({ activeTab }) {
return (
<>
<Activity mode={activeTab === 'analytics' ? 'visible' : 'hidden'}>
<AnalyticsPanel />
</Activity>
<Activity mode={activeTab === 'settings' ? 'visible' : 'hidden'}>
<SettingsPanel />
</Activity>
</>
);
}
With this pattern, an AnalyticsPanel that's tucked away in a background tab doesn't refetch its data or lose its scroll position when you switch away and back — it was simply sitting idle the entire time. Activity in React 19.2 works in two modes: in hidden mode, children are visually hidden but not removed from the tree — their effects stay mounted, but all pending updates are deferred until there's genuinely nothing more urgent to process; in visible mode, children render normally with no interference to how updates are processed. A useful side effect of this design is that you can pre-render tabs the user hasn't even clicked on yet.
Real-world use cases:
- Forms where users repeatedly navigate back and forth between steps
- Expensive charts or data tables living inside tabbed interfaces
- Screen stacks resembling mobile navigation patterns, similar to React Navigation
- Warming up the next screen in advance while the user is still on the current one
2. useEffectEvent — Stable Event Logic Inside Effects
If you've ever thrown a function into a useEffect dependency array purely to keep the linter quiet, only to watch your effect fire far more often than it should, this hook solves exactly that problem.
function ChatRoom({ roomId, theme }) {
const onConnected = useEffectEvent(() => {
showNotification(`Connected to ${roomId}`, theme);
});
useEffect(() => {
const connection = createConnection(roomId);
connection.on('connected', () => onConnected());
connection.connect();
return () => connection.disconnect();
}, [roomId]); // theme no longer needs to be here
}
Inside onConnected, you always read the freshest value of theme, yet updating theme alone won't trigger a reconnect to the chat room. This is essentially the official version of a workaround developers have hand-rolled with useRef for years — React now gives it a name and first-class support.
3. Partial Static Rendering — static-speed pages with live data
This is an opt-in feature aimed at teams building server-rendered applications. Whether you're on Next.js, Remix, or working directly with RSC, React 19.2 lets you prerender the static shell of a page in advance and stream in the dynamic pieces as they become ready.
Most of the page gets rendered upfront as an unchanging shell. Whatever depends on a network call gets filled in afterward and merged in through streaming once the data arrives. That means visitors see your navigation bar and hero section instantly, while anything personalized shows up moments later, without forcing a full-page spinner in between.
The Takeaway
The core message behind React 19.2 is that this isn't a dramatic rewrite of the framework — it's React correcting assumptions baked into its model roughly a decade ago. Hiding something shouldn't force you to unmount it. Writing effects shouldn't mean picking between stale values and unnecessary re-execution. And the speed of your first paint shouldn't be held hostage by your slowest API response.
If your application has felt sluggish in ways that useMemo and React.memo never fully addressed, this release is likely aimed squarely at that gap. Try applying one of these patterns to a tabbed view in your own project this week — the change is small, but the impact on perceived performance is anything but.