Skip to content

React 19.2: faster, smarter and built for performance

Vinicios Neves·Tech Lead

Published on

Hello there 👋

The React Team clearly didn’t want 2025 to go unnoticed — React 19.2 just dropped, and it’s packed with some solid surprises. This is the third update in the React 19 line (19.0 landed back in December, 19.1 in June), and honestly, this one closes the year on a high note.

My goal here is to walk you through what’s new, in plain English — no fluff, no buzzwords, just real-world examples and insights for those already knee-deep in React code.

What’s new in React

<Activity />

This is one of the coolest new components in this release. It basically lets you split your app into “activities” — parts that can be activated, deactivated, or even pre-rendered without affecting the rest of the app.

Before, you’d probably do something like this:

{isVisible && <Page />}

Now you can do:

<Activity mode={isVisible ? 'visible' : 'hidden'}>
  <Page />
</Activity>

Why it matters: the hidden mode unmounts effects and pauses updates, allowing React to focus on what’s actually visible. This is great for preloading pages or preserving the state of views that the user might return to.

Use it when you want smoother transitions between screens, to preload parts of the app, or to keep the state of hidden components intact.

useEffectEvent

If you’ve ever been frustrated with useEffect re-running because of dependencies that didn’t need to be there (like a theme change reconnecting your chat), this one’s for you.

Here’s the classic example:

useEffect(() => {
  const connection = createConnection(serverUrl, roomId);
  connection.on('connected', () => {
    showNotification('Connected!', theme);
  });
  connection.connect();
  return () => connection.disconnect();
}, [roomId, theme]);

Change the theme → chat reconnects. Not fun.

Now, with useEffectEvent:

const onConnected = useEffectEvent(() => {
  showNotification('Connected!', theme);
});

useEffect(() => {
  const connection = createConnection(serverUrl, roomId);
  connection.on('connected', onConnected);
  connection.connect();
  return () => connection.disconnect();
}, [roomId]);

The takeaway: useEffectEvent separates events from effects. That means fewer unnecessary re-renders and no need to fight your linter anymore.

Make sure to update to eslint-plugin-react-hooks@6.1.1 — it’s already aware of this new behavior.

cacheSignal

If you’re working with React Server Components, this one’s for you. cacheSignal lets you detect when a cache() entry expires, so you can abort any related work.

import { cache, cacheSignal } from 'react';
const dedupedFetch = cache(fetch);

async function Component() {
  await dedupedFetch(url, { signal: cacheSignal() });
}

It’s handy for cleaning up or aborting fetches when a render is canceled or completed — a small but powerful addition for server-heavy apps.

Performance Tracks (DevTools)

Chrome DevTools now shows custom React tracks. You can see what React is working on, when it’s paused, what’s prioritized, and how long each part takes.

The Scheduler track shows ongoing work (like blocking tasks or transitions), while the Components track highlights when components mount, update, or get blocked. Open the Performance tab and get ready for a new level of visibility into your app’s behavior.

React DOM: fresh updates

Before diving in, it’s worth saying — these new DOM features make SSR and static rendering feel smoother and more flexible than ever.

Partial Pre-rendering

You can now pre-render parts of your app and resume them later. Perfect for SSR and SSG — render a static shell, send it to your CDN, and React will fill in the rest later.

const { prelude, postponed } = await prerender(<App />, { signal: controller.signal });
await savePostponedState(postponed);

Then, resume with:

const postponed = await getPostponedState(request);
const stream = await resume(<App />, postponed);

In short: SSR and streaming just got faster and more flexible — React can now “pause” and “resume” rendering sections of your app.

Other improvements and fixes

Suspense in SSR now batches boundary reveals, making page loading smoother and aligning client/server behavior. React also adds full Web Streams support in Node.js, although Node Streams remain faster and are still the recommended option.

eslint-plugin-react-hooks v6 now ships with flat config and React Compiler support. Also, the default useId prefix changed from :r: to _r_ to better support View Transitions.

What this means in practice

Overall performance is up, especially for SSR and pre-rendering. useEffectEvent finally fixes one of the most common headaches when refactoring effects. <Activity /> changes how we think about visual lifecycles. And DevTools gives you more insight than ever into what React is doing under the hood.

Before updating

Update eslint-plugin-react-hooks to version 6.1.1. If you’re using SSR in Node, prefer Node Streams (renderToPipeableStream). And double-check Suspense behavior if your app relies on streaming.

Wrapping up

React 19.2 isn’t a revolution, but it’s the kind of release that shows where the React Team is headed — better performance, stronger predictability, and more tools to help you understand what’s going on behind the scenes.

It’s React growing up, without losing that declarative charm we’ve all come to love.

Live long and prosper.

At ustwo, we're always exploring the latest technologies to build better, more performant applications for our clients. If you'd like to have a chat about working with ustwo or joining our team, head over to ustwo.com

About the author:

Vinicios Neves headshot

Vinicios Neves

Tech Lead - Braga, PT

Tech Lead and Educator, has been blending code and teaching for over a decade. A TypeScript specialist, he leads full-stack teams and inspires future developers at FIAP and Alura. With one foot in code and the other in education, he proves that true software engineering goes beyond lines of code. And, of course, he’s a senior expert at saying “it depends.”