In December 2024 the React team shipped React 19 as a stable release, closing out a long canary period that had quietly been running in production at large frameworks for months. For most teams the headline is not a single marquee feature—it is that a pile of things that used to require libraries, boilerplate, or careful hand-wiring are now first-class: form Actions, a hook that reads promises and context, refs passed as ordinary props, and a real cleanup story for ref callbacks. The flip side is a short list of long-deprecated APIs that React 19 finally deletes outright. As the CTO at Softechinfra, I lead the upgrade decisions across our web development projects, and the question every January is the same: do we move now, and how do we do it without a risky big-bang weekend? This is the checklist we actually run—what to verify before you start, what breaks, what to adopt, and how to upgrade incrementally so production never feels it.
Should You Migrate Now At All
Before the mechanics, the decision. React 19 is stable, but "stable" is permission, not obligation. The honest version of the question is whether the upgrade buys you anything this quarter beyond staying current.
Migrate sooner if…
You hand-roll form submission state, you wire forwardRef through every component, you fetch-in-effect for data you already have as a promise, or you are starting new screens this cycle.
Wait a beat if…
You depend on a UI library that has not certified React 19, you still ship class components with string refs or legacy context, or you have no automated tests guarding your render paths.
Decide per app, not per org
In a monorepo, score each app on dependency readiness and test coverage independently. A marketing site and a payment dashboard do not deserve the same risk appetite.
The durable principle here outlives React 19 specifically: a framework upgrade is a risk-managed change, not a chore you batch. Score the benefit, score the blast radius, and let the two numbers—not the changelog's enthusiasm—decide the order you move your apps.
Step One: Land on the Bridge Release First
The single most useful migration tactic is to not migrate to 19 directly. The React team published an intermediate release that adds warnings for everything 19 removes while still running the old behavior. You upgrade to that bridge version first, fix every warning in your console and CI, and only then bump to 19. By the time you flip the major version, you have already done the work—the bump itself becomes a non-event.
This is the same incremental philosophy we apply to every dependency jump, and it pairs naturally with the dependency hygiene we cover in our Next.js performance guide: upgrade the things that warn before you upgrade the thing that breaks.
Step Two: Clear the Removed and Changed APIs
React 19 deletes APIs that have carried deprecation warnings for years. This is the part of the migration that can break a build, so handle it before you reach for any new feature. Run through this list and resolve each item on the bridge release:
- String refs are gone. Replace
ref="myInput"with a callback ref oruseRef. There is no compatibility shim—these simply stop working. - Legacy context is gone. The old
contextTypes/childContextTypesAPI on class components must move tocreateContext. - Module-pattern factory components are gone. A function that returns an object instead of JSX must become a real component or a plain function.
- defaultProps on function components is gone. Use ES default parameters instead;
defaultPropssurvives only on class components. - propTypes are ignored. They no longer throw, but React stops reading them. Move type safety to TypeScript or runtime validation.
- ReactDOM.render and hydrate are removed. Switch to
createRootandhydrateRootif you somehow still hadn't. - The JSX transform is now required. If your build still uses the classic transform, update it or you will hit errors.
Step Three: Adopt the New Capabilities Deliberately
With the breakage cleared, the upside is the reason you upgraded. Adopt these on new code first; do not schedule a sweeping rewrite of working screens just to use them.
Actions and the form-state hooks
Actions are the headline. An async function passed to a form (or invoked from a transition) gets first-class handling of pending state, errors, and optimistic updates—work that used to mean a useState for loading, another for error, and a try/finally to reset them. The supporting hooks make it concrete:
| Hook | What it replaces | Use it for |
|---|---|---|
useActionState |
Manual pending/error/result state around a submit handler | Form submissions with server or async validation |
useFormStatus |
Prop-drilling "is submitting" down to a button | Submit buttons that need parent form status |
useOptimistic |
Hand-rolled optimistic UI with rollback bookkeeping | Likes, reorders, anything that should feel instant |
The use() hook
use() reads a resource during render—a promise (typically surfaced through Suspense) or a context value. Unlike other hooks it can be called conditionally and inside loops, which makes conditional context reads clean. It does not replace your data layer; it gives Suspense a more direct way to consume promises you already have.
Ref as a prop
Function components can now accept ref as a normal prop. For the common case, forwardRef is no longer necessary—a meaningful boilerplate cut in any design system with dozens of wrapped primitives. Keep existing forwardRef usage working; just stop reaching for it in new components.
For the full feature tour—including the smaller wins like document metadata support, stylesheet precedence, and better hydration error messages—our React 19 features breakdown goes deeper than a migration checklist needs to.
The Incremental Rollout We Actually Run
A migration plan is only as good as the order of operations. Here is the sequence we follow so production never absorbs the risk in one jump:
Pin and inventory dependencies
List every package that touches React internals—UI kits, animation libraries, state and data tools. Confirm each declares React 19 support before you start. Unsupported peers are your real schedule, not React itself.
Upgrade to the bridge release in a branch
Get the deprecation warnings flowing in dev and CI. Fix the removed-API list above until the console is silent. Do not merge with warnings outstanding.
Run codemods, then read every diff
The official codemods handle the mechanical edits well. They are a starting point, not an authority—review each change, because automated rewrites of ref and context code occasionally guess wrong.
Bump to 19 behind tests and a preview deploy
With the bridge work done, flip the version. Lean on your test suite and a preview environment so the team exercises real flows before anything reaches users. Strong API contracts make this far calmer—see our API design guide.
Adopt new features on new code only
Reach for Actions, use(), and ref-as-prop in the next features you build. Refactor existing screens only when you are already touching them for another reason.
We followed exactly this path on ExamReady, our exam-preparation platform, where a dense set of timed-question and answer-review forms made Actions and useActionState a genuine reduction in component code—but only after the dependency inventory and the bridge-release cleanup were fully green. The migration shipped across several routine releases, and not one of them was a "React upgrade" release that users could feel.
The Principle That Outlasts This Version
React 19 will be old news soon enough; the next major will arrive with its own removed APIs and its own tempting features. The method does not age. Land on the deprecation-warning bridge first so the major bump is anticlimactic. Separate what breaks from what is optional, and never let the optional column dictate your schedule. Pin and verify dependencies before you touch the framework. Adopt new capabilities on new code, not in a heroic rewrite. A team that runs framework upgrades this way—small, ordered, test-backed—stays current without ever betting a weekend on it. Write your bridge-release branch this week; it is the cheapest insurance in the whole migration.
Planning a React or Next.js Upgrade?
We help product teams modernize React frontends without big-bang risk—dependency audits, incremental migration plans, and the test coverage that makes version bumps boring.
Talk to Our Web Team →
