October 6, 2025
React shipped 19.2 with real, fun goodies (hello <Activity />
, useEffectEvent
, and SSR improvements), while Next.js 15.5 quietly added a first-class switch for the React Compiler. Put together, you can modernize rendering patterns, trim re-renders, and get cleaner Effects without turning your codebase into a memoization shrine. (React)
tldr; Upgrade your app to React 19.2, flip on the (experimental) React Compiler in Next 15.5, and adopt the new hooks/features incrementally. Expect fewer manual
useMemo
/useCallback
, better SSR, and saner Effect event handling. (React)
Update your React packages:
# pnpm / npm / yarn. Pick your poison
pnpm up react@19.2.0 react-dom@19.2.0
pnpm up eslint-plugin-react-hooks@^6.1.1
Why these versions?
React 19.2
delivers new features and SSR fixes (plus a useId
prefix change worth snapshot-testing).eslint-plugin-react-hooks v6
ships compiler-aware lint rules and flat-config presets that play nicely with the new patterns. (React)If you’re on Next 15.x already, bump to 15.5.x
to get the best DX and docs parity:
pnpm up next@15.5.4
Next 15.5 includes infra and DX updates (Turbopack builds in beta, stable Node middleware, TS improvements) you’ll want in daily use. (Next.js)
useEffectEvent
Move “event-like” logic out of Effects without breaking dependency rules.
import { useEffect, useEffectEvent } from 'react'
function ChatRoom({ roomId, theme }: { roomId: string; theme: string }) {
const onConnected = useEffectEvent(() => {
// always sees latest props/state
showToast(`Connected with theme ${theme}`)
})
useEffect(() => {
const conn = connect(roomId)
conn.on('connected', onConnected)
return () => conn.disconnect()
}, [roomId])
}
Result: fewer spurious reconnects and no “disable the lint rule" moments. Update your lint config to use plugin:react-hooks/recommended
(or recommended-legacy
if you need the old behavior). (React)
<Activity />
Pre-render and keep hidden UI “warm” without clobbering visible work. It's useful for near-future navigations.
import { Activity } from 'react'
export default function Shell({ prefetchNext }: { prefetchNext: boolean }) {
return (
<Activity mode={prefetchNext ? 'hidden' : 'visible'}>
<NextPageLikelyNeededSoon />
</Activity>
)
}
This helps with snappy navs and state preservation when users bounce back and forth. (React)
Notes worth snapshot-testing: -
useId
now prefixes with_r_
(changed again in 19.2). If you assert on IDs in tests or CSS selectors, adjust. (React)
The React Compiler auto-memoizes components/hooks so you write less useMemo
/useCallback
and still avoid wasteful re-renders.
Install:
pnpm add -D babel-plugin-react-compiler
Turn it on in
next.config.ts:
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
reactCompiler: true, // or { compilationMode: 'annotation' } for opt-in
},
}
export default nextConfig
Next uses a custom SWC optimization to apply the compiler only where it matters, keeping builds fast. You can also run in “annotation” mode and opt in per file with "use memo"
(and opt out with "use no memo"
). (Next.js)
Reality check: In Next docs, the compiler is
experimental and not recommended for production
(yet). Try it behind an env flag and measure. (Next.js)
"use memo"
to a leaf component and confirm render counts drop in React DevTools Profiler.React Performance Tracks
that make it easier to see scheduler and component work. Expect clearer traces when hidden/visible work is separated. (React)If enabling globally feels spicy, switch to annotation mode
:
experimental: {
reactCompiler: { compilationMode: 'annotation' },
}
Then opt-in surgically:
export function ExpensiveRow({ item }: { item: Item }) {
'use memo' // let the compiler lock this down
// heavy render...
}
Start with:
useCallback
/useMemo
.As you gain confidence, consider flipping the global true
and opt out
where needed with "use no memo"
. (Next.js)
Mutation & impurity still bite.
The compiler assumes React’s “keep components pure” guidance. Mutating props or external objects can produce head-scratchers, fix the code, don’t fight the tool. (See the compiler docs’ troubleshooting and “keeping components pure”.) (React)Third-party libs.
If a lib couples rendering with mutation, annotate the boundary ("use no memo"
) or keep that zone uncompiled until you can refactor/replace. (React)Linting.
Upgrade eslint-plugin-react-hooks
and use its recommended config so useEffectEvent
and compiler rules cooperate. (React)Build time.
Expect a small build-time hit. Next’s SWC filtering helps keep it modest. Track CI minutes after enabling. (Next.js)Before (manual memo)
// Lots of boilerplate that may or may not be correct:
const Cell = React.memo(function Cell({ value, onClick }) {
const expensive = useMemo(() => crunch(value), [value])
const handle = useCallback(() => onClick(expensive), [onClick, expensive])
return <button onClick={handle}>{expensive.label}</button>
})
After (let the compiler work)
export function Cell({ value, onClick }: { value: X; onClick: (x: X) => void }) {
'use memo' // if using annotation mode, omit if globally enabled
const expensive = crunch(value) // compiler can memoize this
const handle = () => onClick(expensive) // compiler can stabilize this
return <button onClick={handle}>{expensive.label}</button>
}
Cleaner and similar perf once compiled. (Measure in your app!)
Turbopack builds (beta):
experiment on CI, faster cold builds are nice for PR velocity.Stable Node.js Middleware:
unify edge vs node handling where it makes sense.Deprecations (
next lint, etc.):
scan release notes and clean up warnings to avoid 16.x surprises. (Next.js)Rollout plan:
Start with a canary env + traffic sampling. Flip reactCompiler
only for that environment.Perf budget:
Add Profiler traces to PRs touching high-churn components. Watch React Tracks in Chrome. (React)Codemod backlog:
After the compiler stabilizes, remove redundant useMemo
/useCallback
where they no longer pull weight.RSC & SSR:
Evaluate 19.2 SSR tweaks (batched Suspense reveals, Web Streams APIs) if you stream HTML. Stick to Node Streams in Node for perf. (React)React 19.2 brings tangible DX and perf wins. the React Compiler promises fewer re-render headaches with far less code. In Next 15.5 the switch is there, just flip it thoughtfully
.