Skip to content
KKAELUM STUDIO
← All entries
02 March 2026 · 2 min read

Core Web Vitals after React 19

What changed with React 19, what got faster for free, and the patterns we adopted to push INP below 100ms in production.

React 19 has been in production for most of 2025. By now the dust has settled enough to write honestly about its effect on Core Web Vitals — not the launch-day blog posts, the live-site numbers.

INP is the only interesting metric left

LCP and CLS are largely solved problems if you ship a modern stack. Image formats, layout reservations, font preload — these are checklists, not engineering challenges.

INP — Interaction to Next Paint — is the one that still separates competently-built sites from carelessly-built ones. And it's the one React 19's compiler and Actions story has changed most.

What got faster for free

The React Compiler — production-ready since 19.1 — auto-memoises components that we used to wrap in useMemo and useCallback. In practice this means:

  • We deleted ~40% of our manual memoisation in motion-heavy components.
  • Re-renders in dense lists (case study indices, journal cards) are noticeably cheaper.
  • Bundle size went up a couple of kilobytes from the compiler runtime, but TBT went down by 60–120ms on listing pages.

What we changed deliberately

The compiler is necessary but not sufficient. The patterns that took us from "good" INP to consistently green on every flagship build:

  1. Server Actions for everything write-path. Contact forms, newsletter, comment posting — no JSON API roundtrip, no form library state machinery on the client.
  2. use for streaming non-critical data. Suspense boundaries around testimonials, journal teasers, social-proof strips. The hero paints in 800ms regardless of the slow data.
  3. No third-party JavaScript in the document head. Analytics is loaded with next/script strategy="lazyOnload". Tag managers are negotiated out of the brief.
  4. One animation library. Switching between Framer, GSAP, and CSS made our hydration trees inconsistent. Today: Motion (formerly framer-motion) v12 for component-level animation, GSAP only for ScrollTrigger-style pinning, Lenis for smooth scroll. That's it.

The metric we care about

Our internal target on every shipped site:

  • LCP < 1.2s (lab) · < 2.0s (field p75)
  • INP < 100ms (field p75)
  • CLS < 0.02

We measure with Vercel Analytics for the field, and with Lighthouse CI in the pipeline as a hard gate. A new build cannot ship if the lab numbers regress beyond a 3-point threshold.

What's still hard

Two things, even in 2026:

  • Third-party scripts you don't control. Klaviyo, Recharge, certain Shopify apps. These are getting better but they are still where most INP regressions come from.
  • Long lists with sticky animations. The pattern we use for the Featured Work index had to be rewritten three times before it stayed under 100ms INP on a mid-tier Android.

If you're shipping a site in 2026 and your INP is 250ms, it isn't React. It's almost always a script you can remove, a memoisation you can delete, or an animation library you should pick one of.