
Introduction to React 19: Breaking Changes, Deprecations & Migration Guidance
React 19 does introduce some breaking changes – mostly the removal of long-deprecated APIs. The React team has provided an upgrade guide and codemods to help migrate. Here’s a summary of what’s changed and how to deal with it:
PropTypes and DefaultProps (Function Components) Removed: PropTypes haven’t been part of core React since 2017 (moved to a separate
prop-types
package) and were officially deprecated then (React 19 Upgrade Guide – React). In React 19, using PropTypes on a component will simply do nothing – the runtime type checks are removed (React 19 Upgrade Guide – React). Similarly,defaultProps
on function components is no longer supported (it will be ignored) (React 19 Upgrade Guide – React). The recommendation is to use default function parameters or destructuring default values for default props, or switch to TypeScript for type checking (React 19 Upgrade Guide – React) (React 19 Upgrade Guide – React). Class components can still use defaultProps (since there’s no alternative for them), but function components should migrate. A codemod is available to help convert PropTypes to TypeScript definitions (React 19 Upgrade Guide – React).Legacy Context Removed: The old context API (
MyComponent.childContextTypes
andcontextTypes
withgetChildContext
) is gone in React 19 (React 19 Upgrade Guide – React). This was deprecated way back in 16.3 (2018) in favor of the new Context API. If you have any class components usingcontextTypes
/getChildContext
, you must refactor them to use either the new Context (viacontextType
on class or<Context.Provider>
usage) (React 19 Upgrade Guide – React) (React 19 Upgrade Guide – React). The upgrade guide provides examples for this migration (React 19 Upgrade Guide – React) (React 19 Upgrade Guide – React). Fortunately, very few apps still used legacy context by 2024, but if you’re upgrading a really old codebase, this could be one of the bigger tasks.String Refs Removed: Using a string like
ref="myRef"
in JSX was deprecated in 16.3 and now is completely removed (React 19 Upgrade Guide – React). If you have<Component ref="name">
, it will no longer work – you need to replace it with callback refs orcreateRef
. A codemod is available to automate this to the function form (ref={el => this.name = el}
) (React 19 Upgrade Guide – React) (React 19 Upgrade Guide – React). String refs had numerous issues (they were not type-safe, and they were slow) (React 19 Upgrade Guide – React) (React 19 Upgrade Guide – React), so React 19 dropping support is a net positive.Module Pattern Components Removed: This is a very rare pattern where a function component returns an object with a render method (essentially a factory that creates an object component). It was deprecated in 16.9. React 19 no longer supports it (React 19 Upgrade Guide – React) (React 19 Upgrade Guide – React). If you had code like
function CompFactory(){ return { render(){...} } }
, convert it to a regular function component that returns JSX instead (React 19 Upgrade Guide – React) (React 19 Upgrade Guide – React).React.createFactory
Removed: The old utility to create a component factory (before JSX was common) is removed (React 19 Upgrade Guide – React). If you have any usages ofReact.createFactory(Component)
, you can replace them with just using<Component />
JSX or callingReact.createElement
. This likely won’t affect many, as it’s quite an antiquated API (from the early React 0.x days).Shallow Renderer Moved: The
react-test-renderer/shallow
export has been removed. In React 18 it was already moved out to a separate package (react-shallow-renderer
) and just re-exported with a warning (React 19 Upgrade Guide – React). In React 19, you’ll need to import from that package directly if you need shallow rendering (React 19 Upgrade Guide – React) (React 19 Upgrade Guide – React). The React team strongly suggests not using shallow rendering at all, as it’s not compatible with newer features and can give a false sense of security in tests (React 19 Upgrade Guide – React) (they recommend testing library instead).ReactDOM.test-utils
Removals: Aside fromact
being moved, all other old test utils likeSimulate
are removed (calling them will error out) (React 19 Upgrade Guide – React). These were rarely used outside legacy Enzyme tests. Modern advice is to simulate events via actual DOM events or user-event library rather thanSimulate.click
etc.Old Rendering APIs Removed: This one will affect almost everyone upgrading: the old ReactDOM rendering APIs are gone. Specifically:
-
ReactDOM.render
– removed. Must usecreateRoot(container).render()
instead (React 19 Upgrade Guide – React) (React 19 Upgrade Guide – React). -
ReactDOM.hydrate
– removed. Must usehydrateRoot(container, element)
instead (React 19 Upgrade Guide – React) (React 19 Upgrade Guide – React). -
ReactDOM.unmountComponentAtNode
– removed. Must use theroot.unmount()
from the root you created (React 19 Upgrade Guide – React) (React 19 Upgrade Guide – React). -
ReactDOM.findDOMNode
– removed. This one is important: if you have any code (often in older class components) that doesfindDOMNode(this)
, it will no longer work (React 19 Upgrade Guide – React) (React 19 Upgrade Guide – React). The replacement is to use refs. This might require a bit of refactoring for older components (e.g., instead of findDOMNode to get a DOM node, attach a ref to that node directly).
These APIs were all deprecated in React 18.0 back in 2022 (React 19 Upgrade Guide – React) (React 19 Upgrade Guide – React), so hopefully you’ve seen the warnings. If not, React 18.3 (the helper release) prints warnings whenever you use them, to alert you before jumping to 19 (React 19 Upgrade Guide – React). There are codemods to automate some of this (like replacing
ReactDOM.render
calls) (React 19 Upgrade Guide – React). The motivation is to fully embrace the new root API which is required for concurrency features and is less error-prone. One notable adjustment: if your app relied onunmountComponentAtNode(node)
in some cleanup code, you now need to keep a reference to the root and callroot.unmount()
. If you had a portal that you manually rendered, you’ll do something similar. It’s a mechanical change but make sure to test these flows after upgrading.-
Deprecations in React 19 (to be removed later):
-
element.ref
– In React, when you create an element (JSX), it has a property.ref
if a ref was attached. This is an internal thing you typically don’t use. In React 19, since ref is now just a prop, accessingelement.ref
is deprecated (and will warn) (React 19 Upgrade Guide – React) (React 19 Upgrade Guide – React). In a future release,element.ref
will likely be removed entirely. This shouldn’t affect most userland code unless you were doing something very unusual (like reading the ref on a JSX element, which one usually doesn’t do). -
react-test-renderer
– The entire package isn’t removed yet, but it’s deprecated as of 19 (React 19 Upgrade Guide – React) (React 19 Upgrade Guide – React). It will log a warning if you use it. The reason is similar to shallow rendering – the test renderer doesn’t run React the same way as the real DOM, leading to non-representative results. React 19’s test renderer also now uses concurrent mode by default, which could break some tests that weren’t expecting it. The team suggests migrating to React Testing Library for more realistic tests (React 19 Upgrade Guide – React) (React 19 Upgrade Guide – React). They haven’t given a timeline, but likely React 20 might removereact-test-renderer
.
-
TypeScript Changes: If you use TS, you’ll need to update
@types/react
and@types/react-dom
to 19.x as well. Those types have removed the definitions for the deprecated APIs (so TS will error if you try to use them, guiding you to update) (React 19 Upgrade Guide – React) (React 19 Upgrade Guide – React). Some types have moved, e.g., the types for the test utils moved to thereact-dom
types or were deleted. Also, as mentioned, the type of ref callbacks is stricter (must return void or a cleanup function, not an element) (React v19 – React).Migration Strategy: The recommended path is:
1. **Upgrade to React 18.3** (which is identical to 18.2 but with added deprecation warnings) ([React 19 Upgrade Guide – React](https://react.dev/blog/2024/04/25/react-19-upgrade-guide#improvements-to-suspense#:~:text=,been%20published)). Run your app and see the console for warnings. Fix those proactively (they correspond to things that break in 19). For example, if you see warnings about `findDOMNode` or `ReactDOM.render`, refactor those parts.
2. Use the official **codemods**. The React team released a set of codemod scripts for v19 ([React 19 Upgrade Guide – React](https://react.dev/blog/2024/04/25/react-19-upgrade-guide#improvements-to-suspense#:~:text=Changes%20that%20include%20a%20codemod,include%20the%20command%20below)). They can auto-transform a lot of legacy patterns (PropTypes to TS, react-dom render to createRoot, string refs, etc.). Always commit your code before running them and review changes after.
3. Install React 19 and ReactDOM 19. If you’re using a package manager lockfile, make sure to get 19.0.x specifically (sometimes a `next` tag might still point to RCs, but now it should be stable).
4. Run your test suite and manual tests. Pay special attention to any code that might interact with those removed APIs.
The upgrade guide stated: _“we don’t expect the changes to impact most apps”_ ([React 19 Upgrade Guide – React](https://react.dev/blog/2024/04/25/react-19-upgrade-guide#improvements-to-suspense#:~:text=The%20improvements%20added%20to%20React,changes%20to%20impact%20most%20apps)) – and indeed, if you were keeping up with deprecations, you might find very little to change. Many apps mostly needed to update their root entry point (to use `createRoot`) and remove any stray `findDOMNode` or PropTypes usage. The effort is nowhere near something like the migration from React 15 to 16 or 16 to 17, thanks to the gradual deprecation strategy.
- Breaking changes that might not be obvious: One behavioral change to call out: the event handling around forms. Because Actions handle form submissions differently (e.g., auto-resetting forms), if you adopt them, you might need to adjust tests that expected form fields to stay filled after submit. Also, if you had a custom
<Form>
component that spread props to a real<form>
, note that aaction
prop is now special. React uses it for this new behavior. If your component wasn’t expecting anaction
prop (maybe you thought it was an HTML attribute or something), it could be impacted. Similarly, the built-in<form>
element’s type definitions might now show thataction
can accept a function (in addition to string), at least in a React context.
Conclusion
To sum up, React 19’s breaking changes are mostly deletions of things you shouldn’t be using anymore. The React team gave a long runway (some were deprecated 5+ years ago). The new stuff is added in a backwards-compatible way for the most part. By using the 18.3 bridge release and codemods, most projects reported fairly smooth upgrades. Keep an eye on your dependencies too – libraries like React Router, React Redux, etc., released patches to remove their usage of deprecated internals so that they wouldn’t block React 19. As the upgrade guide humorously noted, some libraries were reaching into SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
and got “fired” by these changes (React 19 Upgrade Guide – React) (React 19 Upgrade Guide – React) – hopefully none of your dependencies are doing that, but if so, you’d need to update those libs as well.
Date: