
Learn what's new in React 19.2: Activity for state-preserving UI, useEffectEvent for cleaner effects, compiler-powered ESLint rules, and Performance Tracks in DevTools.
Aurora Scharff
February 2, 2026
React 19.2 is now available on npm. This release follows React 19 (December 2024) and React 19.1 (June 2025).
This article covers the most practical new features and changes for regular React developers: <Activity>, useEffectEvent, the upgraded ESLint plugin, and Performance Tracks.
<Activity> lets you hide parts of your app while preserving their state. Think of it as a smarter alternative to conditional rendering.
Before, you'd conditionally render components like this:
{isVisible && <Page />}
The problem? When isVisible becomes false, React unmounts <Page /> entirely. Any internal state (form inputs, scroll position, expanded sections) is lost.
With <Activity>, you can hide content while keeping it alive:
import { Activity } from 'react';
<Activity mode={isVisible ? 'visible' : 'hidden'}>
<Page />
</Activity>
<Activity> supports two modes:
visible: Shows children, mounts effects, processes updates normallyhidden: Hides children with display: none, unmounts effects, defers updates until React is idleWhen hidden, React cleans up effects (subscriptions, timers, etc.) but preserves the component's state and DOM. When it becomes visible again, effects are re-created and the UI is restored exactly as it was.
Preserving tab state: Instead of unmounting inactive tabs, keep them hidden so users don't lose their work when switching back:
function TabPanel({ activeTab }) {
return (
<>
<Activity mode={activeTab === 'home' ? 'visible' : 'hidden'}>
<Home />
</Activity>
<Activity mode={activeTab === 'contact' ? 'visible' : 'hidden'}>
<Contact />
</Activity>
</>
);
}
If a user types a draft message in the Contact tab, switches to Home, then switches back, the draft is still there.
Pre-rendering content: Hidden <Activity> boundaries render their children at low priority. This lets you pre-load data or components the user is likely to navigate to:
function App() {
const [activeTab, setActiveTab] = useState('home');
return (
<Suspense fallback={<Loading />}>
<Activity mode={activeTab === 'home' ? 'visible' : 'hidden'}>
<Home />
</Activity>
<Activity mode={activeTab === 'posts' ? 'visible' : 'hidden'}>
<Posts /> {/* Pre-renders and fetches data while hidden */}
</Activity>
</Suspense>
);
}
When the user clicks the Posts tab, the content appears instantly because it was already rendered in the background.
Faster hydration: Activity boundaries help React hydrate your app in chunks. Even if you never hide content, wrapping lower-priority sections in <Activity> lets React hydrate interactive elements first:
function Page() {
return (
<>
<Post />
<Activity>
<Comments /> {/* Hydrates after Post */}
</Activity>
</>
);
}
Note: Next.js uses <Activity> for client-side navigation when Cache Components is enabled. Rather than unmounting the previous route when you navigate away, Next.js sets the Activity mode to "hidden". This preserves component state (form inputs, expanded sections) when users navigate back and forth between routes. See the Next.js docs for details.
Since hidden Activity boundaries preserve DOM but clean up effects, some elements like <video> may continue playing after being hidden. Add cleanup logic in your effects:
function VideoTab() {
const ref = useRef();
useLayoutEffect(() => {
const videoRef = ref.current;
return () => {
videoRef.pause(); // Pause when hidden
};
}, []);
return <video ref={ref} controls src="..." />;
}
Use useLayoutEffect instead of useEffect for cleanup tied to visibility changes. It runs synchronously before the content is hidden.
One of the most annoying patterns with useEffect is when you need to read a value inside an effect but don't want changes to that value to re-run the effect.
Here's a common scenario: showing a notification when connecting to a chat room:
function ChatRoom({ roomId, theme }) {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.on('connected', () => {
showNotification('Connected!', theme); // Uses theme
});
connection.connect();
return () => connection.disconnect();
}, [roomId, theme]); // theme is a dependency
}
The problem: changing theme reconnects to the chat room, even though theme has nothing to do with the connection logic. You only need the current theme when the "connected" event fires.
Most developers disable the lint rule or ignore the warning. But that leads to bugs when dependencies actually matter.
useEffectEvent extracts the "event" part out of your effect:
import { useEffect, useEffectEvent } from 'react';
function ChatRoom({ roomId, theme }) {
const onConnected = useEffectEvent(() => {
showNotification('Connected!', theme);
});
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.on('connected', () => {
onConnected();
});
connection.connect();
return () => connection.disconnect();
}, [roomId]); // ✅ theme is no longer a dependency
}
Now theme changes don't re-run the effect. The onConnected function always "sees" the latest theme value when it's called, but it's not reactive. It won't trigger re-execution.
Use useEffectEvent for functions that are conceptually "events" fired from an effect: notifications, analytics, logging, or any callback where you need current values but don't want to re-synchronize.
Another example: logging page visits without re-logging when cart items change:
function Page({ url }) {
const { items } = useContext(ShoppingCartContext);
const numberOfItems = items.length;
const onVisit = useEffectEvent((visitedUrl) => {
logVisit(visitedUrl, numberOfItems); // Always reads latest numberOfItems
});
useEffect(() => {
onVisit(url);
}, [url]); // ✅ Only re-runs when url changes
}
The ESLint plugin will help enforce these rules.
The familiar eslint-plugin-react-hooks has been upgraded with React Compiler diagnostics. Even if you haven't adopted the compiler yet, these new rules help you write code that's ready for automatic optimization.
npm install -D eslint-plugin-react-hooks@latest
The plugin now uses flat config by default:
// eslint.config.js
import reactHooks from 'eslint-plugin-react-hooks';
export default [
reactHooks.configs.flat.recommended,
];
If you're still using legacy config:
// .eslintrc.js
{
extends: ['plugin:react-hooks/recommended-legacy']
}
In addition to the classic rules-of-hooks and exhaustive-deps, the plugin now includes compiler-powered rules that catch patterns breaking memoization:
setState calls during rendersetState in effectsHere are some examples of what the linter catches:
// ❌ Calling setState during render causes infinite loops
function Counter() {
const [count, setCount] = useState(0);
setCount(count + 1); // Error: setState in render
return <div>{count}</div>;
}
// ❌ Reading a ref during render is unsafe
function Input() {
const inputRef = useRef(null);
const value = inputRef.current?.value; // Error: ref access in render
return <input ref={inputRef} />;
}
// ❌ Mutating props breaks memoization
function List({ items }) {
items.sort(); // Error: mutating props
return <ul>{items.map(item => <li key={item}>{item}</li>)}</ul>;
}
These rules catch patterns that break the Rules of React, which can cause bugs in any React app. You don't need to adopt React Compiler to benefit from them. If you do use the compiler, it will automatically skip components with violations while optimizing the rest of your app. Either way, you can fix violations at your own pace.
For more on React Compiler, see React Compiler: No More useMemo and useCallback.
React 19.2 adds custom tracks to Chrome DevTools' Performance panel, giving you deep visibility into what React is doing. See the Performance Tracks documentation for full details.
To see them, record a performance profile in Chrome DevTools. You'll see new React-specific tracks alongside the standard flame charts.
Shows what React is working on at different priorities:
startTransition (runs in background)Each render shows its phases: Update → Render → Commit → Remaining Effects. You can see when renders are blocked waiting for higher-priority work, or when React is yielding to the browser.
Shows a flamegraph of component render and effect durations. You can:
The Components track uses different colors for render vs. effect phases, matching the Scheduler track's color scheme.
react-dom/profiling instead of react-dom/client. Wrap subtrees in <Profiler> to see them in the Components track (or install React DevTools extension to see all components)Note that profiling adds overhead, so don't use it in production unless specifically measuring performance.
React 19.2 adds the ability to pre-render part of your app ahead of time and resume rendering later. This lets you pre-render static parts of your app and serve them from a CDN, then fill in dynamic content when the request comes in.
The new APIs include prerender with an AbortController to generate a prelude and postponed state, then resume or resumeAndPrerender to complete the render later. This is primarily useful for framework authors building SSG/SSR solutions.
React now batches reveals of server-rendered Suspense boundaries for a short time, allowing more content to appear together. This creates a smoother experience and prepares apps for <ViewTransition> animations during SSR.
The default useId prefix changed from :r: (19.0) and «r» (19.1) to _r_. The new prefix is valid for view-transition-name and XML 1.0 names, which is needed for View Transitions support.
renderToReadableStream and prerender are now available for Node.js. However, Node Streams (renderToPipeableStream, prerenderToNodeStream) are still recommended in Node environments for better performance and compression support.
React 19.2 brings features that solve real problems:
<Activity> lets you hide UI while preserving state, perfect for tabs, modals, and pre-renderinguseEffectEvent fixes the dependency array problem for event-like logic in effectsTo upgrade, run:
npm install react@latest react-dom@latest
Sources:
Get the latest news and updates on developer certifications. Content is updated regularly, so please make sure to bookmark this page or sign up to get the latest content directly in your inbox.

What's New in React 19.2
Learn what's new in React 19.2: Activity for state-preserving UI, useEffectEvent for cleaner effects, compiler-powered ESLint rules, and Performance Tracks in DevTools.
Aurora Scharff
Feb 2, 2026

What’s new in Angular 21.1?
Angular 21.1 is out, and while most of the new features are still experimental, the release gives us a solid preview of where Angular is heading. From Signal Forms API changes and long-awaited focus helpers, to more flexible control flow, template improvements, router updates, and MCP server enhancements, this version is packed with ideas worth exploring—just not shipping to production yet. Let’s take a quick look at what’s new and what’s promising.
Alain Chautard
Jan 23, 2026

How to pass your certification exam
Getting ready for the Nuxt certification exam? See how the exam works, what’s on it, and how to prep so you can actually pass it on the first try.
Reza Baar
Jan 22, 2026
We can help you recruit Certified Developers for your organization or project. The team has helped many customers employ suitable resources from a pool of 100s of qualified Developers.
Let us help you get the resources you need.
