Skip to content
Journal

Engineering

React 19 Is Stable. Here Is What Changes for Client Work

Headshot of Alex Fujimoto
Alex Fujimoto
December 10, 2024 · 3 min read

React 19 reached stable last week, closing out the longest release cycle in the library's history. Version 18 shipped in early 2022, and the two and a half years since have been an extended public beta of a new mental model: server components, actions, and a compiler-shaped future. Now the stable tag makes it official, and the question our clients actually care about is not the changelog. It is: what does this change about the sites and products we ship together?

Short answer: less than the discourse suggests, more than a version bump usually does. Here are the field notes.

Actions are the real headline

The feature with the highest impact-to-hype ratio is Actions: first-class support for async mutations, with pending, error, and optimistic states handled by the framework instead of by every team's slightly different useState choreography. Anyone who has audited a mature codebase knows the pattern React 19 deletes: isSubmitting flags, hand-rolled error plumbing, the optimistic update that forgets to roll back.

function RSVPForm() {
  const [state, submitAction, isPending] = useActionState(
    async (prev, formData) => {
      const error = await saveRSVP(formData);
      return error ?? null;
    },
    null
  );
  return (
    <form action={submitAction}>
      <input name="email" type="email" required />
      <button disabled={isPending}>{isPending ? "Sending" : "RSVP"}</button>
      {state && <p role="alert">{state}</p>}
    </form>
  );
}

Forms that work before hydration, progressive enhancement by default, and roughly a third less mutation boilerplate on the dashboards we build. For the commerce and product work that dominates our engineering practice, this is the feature that pays rent.

Quality of life, quantified

The rest of the release is a stack of small dignities. Refs pass as ordinary props, retiring forwardRef and its wrapper noise. Document metadata, title, meta, link, renders from components and hoists itself to the head, which simplifies every CMS-driven page we maintain. The use() API reads promises and context with fewer contortions. Hydration errors now print an actual diff instead of a riddle, which alone will save our team a few hours a month. And server components are now a stable part of React itself rather than a framework-specific adventure, which matters less for what it enables today than for what it settles: the architectural argument is over.

What we are not doing

We are not rewriting anything to be more React 19-shaped. Existing patterns keep working; the upgrade for most codebases is hours, not weeks, especially for teams already on Next.js 15, which has been running the release candidate since October. We are also not adopting the React Compiler in client production yet; it remains beta, and our policy on beta compilers is gratitude from a safe distance.

And we are resisting the urge to migrate every existing form to Actions immediately. New work gets the new patterns. Old work migrates when it is touched for other reasons. Churn without user benefit is how agencies bill hours, and we do not bill hours.

The takeaway for non-engineers

If you are a client reading this, the practical summary: the platform under your product just got calmer. Fewer moving parts in the mutation code means fewer edge-case bugs in checkouts and signups. Progressive enhancement by default means your forms work on hostile networks. And the ecosystem consolidating on one stable foundation means the next two years of React churn should be smaller than the last two.

That is the quiet good news of React 19: it is a release about finishing arguments rather than starting them. After two and a half years, the frontier settled down and became infrastructure. We build better on infrastructure.

Building something this could apply to?

We take on a small number of flagship projects each quarter.

Start a project