React Upgrade Guide

Compare React versions and see exactly what changes, what requires action, and what you can start using.

30Changes
13Breaking
16Actions
12Features
0Migration tools
Analyze your project

Paste your package.json

UpgradePath detects relevant tools and selects them automatically.

Local only
Analyzed locally in your browser. Nothing is uploaded.
Project setup

What does your project use?

Select the tools in your project to include relevant dependency updates in this guide.

Breaking Changes

The modern JSX transform is required

Action required
breakinghigh impact

React 19 requires the modern JSX transform. Projects still using the legacy transform should update their build configuration before upgrading.

Migration

Ensure your build tool uses the modern JSX transform before upgrading to React 19.

propTypes are removed for function components

Action required
breakingmedium impact

React 19 removes runtime propTypes checks for function components. Existing propTypes declarations on function components are ignored.

Beforejsx
import PropTypes from "prop-types";

function Heading({ text }) {
  return <h1>{text}</h1>;
}

Heading.propTypes = {
  text: PropTypes.string,
};
Aftertsx
interface HeadingProps {
  text?: string;
}

function Heading({
  text,
}: HeadingProps) {
  return <h1>{text}</h1>;
}
Migration

Remove function-component propTypes and migrate to TypeScript or another type-checking solution.

defaultProps are removed for function components

Action required
breakingmedium impact

React 19 removes defaultProps support from function components. Class components continue to support defaultProps.

Beforetsx
function Heading({
  text,
}: {
  text?: string;
}) {
  return <h1>{text}</h1>;
}

Heading.defaultProps = {
  text: "Hello",
};
Aftertsx
function Heading({
  text = "Hello",
}: {
  text?: string;
}) {
  return <h1>{text}</h1>;
}
Migration

Replace function-component defaultProps with JavaScript default parameters.

Legacy Context APIs are removed

Action required
breakinghigh impact

React 19 removes the legacy contextTypes and getChildContext APIs that were deprecated in React 16.6.

Beforejsx
class Parent extends React.Component {
  getChildContext() {
    return {
      theme: "dark",
    };
  }

  render() {
    return <Child />;
  }
}

Parent.childContextTypes = {
  theme: PropTypes.string,
};
Aftertsx
const ThemeContext =
  React.createContext("light");

function Parent() {
  return (
    <ThemeContext value="dark">
      <Child />
    </ThemeContext>
  );
}
Migration

Replace contextTypes and getChildContext with createContext and the modern Context API.

String refs are removed

Action required
breakinghigh impact

React 19 removes string refs from class components. Ref callbacks or createRef should be used instead.

Beforejsx
class Form extends React.Component {
  componentDidMount() {
    this.refs.input.focus();
  }

  render() {
    return (
      <input ref="input" />
    );
  }
}
Afterjsx
class Form extends React.Component {
  input = null;

  componentDidMount() {
    this.input?.focus();
  }

  render() {
    return (
      <input
        ref={(input) => {
          this.input = input;
        }}
      />
    );
  }
}
Migration

Replace string refs with callback refs or createRef. React provides a codemod for this migration.

Module pattern factories are removed

Action required
breakinglow impact

React 19 removes support for the legacy module pattern factory component style.

Beforejsx
function FactoryComponent() {
  return {
    render() {
      return <div>Hello</div>;
    },
  };
}
Afterjsx
function FactoryComponent() {
  return <div>Hello</div>;
}
Migration

Convert module pattern factories into normal function components.

React.createFactory is removed

Action required
breakinglow impact

React 19 removes React.createFactory, an API that predates widespread JSX adoption.

Beforejavascript
import {
  createFactory,
} from "react";

const Button =
  createFactory("button");

const element =
  Button({
    children: "Save",
  });
Afterjsx
const element = (
  <button>
    Save
  </button>
);
Migration

Replace React.createFactory usage with JSX.

react-test-renderer/shallow is removed

Action required
breakingmedium impact

React 19 removes the react-test-renderer/shallow entry point. React recommends moving away from shallow rendering.

Beforetypescript
import ShallowRenderer from
  "react-test-renderer/shallow";
Aftertypescript
import ShallowRenderer from
  "react-shallow-renderer";
Migration

Prefer migrating tests to React Testing Library. If shallow rendering must temporarily remain, install react-shallow-renderer directly.

Import act from react instead of react-dom/test-utils

Action required
breakingmedium impact

React 19 moves act to the React package. The react-dom/test-utils version is deprecated and the other legacy test utilities are removed.

Beforetypescript
import {
  act,
} from "react-dom/test-utils";
Aftertypescript
import {
  act,
} from "react";
Migration

Import act directly from react. Review other react-dom/test-utils usage and migrate away from low-level test utilities.

ReactDOM.render is removed

Action required
breakinghigh impact

React 19 removes ReactDOM.render after its deprecation in React 18. Applications must use createRoot.

Beforetsx
import {
  render,
} from "react-dom";

render(
  <App />,
  document.getElementById("root"),
);
Aftertsx
import {
  createRoot,
} from "react-dom/client";

const container =
  document.getElementById("root");

const root =
  createRoot(container!);

root.render(<App />);
Migration

Replace ReactDOM.render with createRoot from react-dom/client. The React 19 migration recipe includes a codemod for this change.

ReactDOM.hydrate is removed

Action required
breakinghigh impact

React 19 removes the legacy ReactDOM.hydrate API. Server-rendered applications must use hydrateRoot.

Beforetsx
import {
  hydrate,
} from "react-dom";

hydrate(
  <App />,
  document.getElementById("root"),
);
Aftertsx
import {
  hydrateRoot,
} from "react-dom/client";

hydrateRoot(
  document.getElementById("root")!,
  <App />,
);
Migration

Replace ReactDOM.hydrate with hydrateRoot from react-dom/client.

unmountComponentAtNode is removed

Action required
breakingmedium impact

React 19 removes ReactDOM.unmountComponentAtNode. Applications using the root API should unmount through the root instance.

Beforetypescript
import {
  unmountComponentAtNode,
} from "react-dom";

unmountComponentAtNode(
  container,
);
Aftertypescript
const root =
  createRoot(container);

root.render(<App />);

// Later:
root.unmount();
Migration

Keep the root returned by createRoot and call root.unmount() when the application needs to be removed.

ReactDOM.findDOMNode is removed

Action required
breakinghigh impact

React 19 removes findDOMNode. The API was a legacy escape hatch that broke component abstraction and was fragile during refactoring.

Beforetsx
import {
  findDOMNode,
} from "react-dom";

useEffect(() => {
  const input =
    findDOMNode(component);

  input?.focus();
}, []);
Aftertsx
const inputRef =
  useRef<HTMLInputElement>(null);

useEffect(() => {
  inputRef.current?.focus();
}, []);

return (
  <input ref={inputRef} />
);
Migration

Replace findDOMNode with an explicit DOM ref.

New Features

Function components can receive ref as a prop

featuremedium impact

React 19 allows function components to access ref directly as a prop, reducing the need for forwardRef in new function components.

Beforetsx
import {
  forwardRef,
} from "react";

const Input = forwardRef<
  HTMLInputElement,
  InputProps
>((props, ref) => {
  return (
    <input
      {...props}
      ref={ref}
    />
  );
});
Aftertsx
function Input({
  ref,
  ...props
}: InputProps & {
  ref?: React.Ref<HTMLInputElement>;
}) {
  return (
    <input
      {...props}
      ref={ref}
    />
  );
}
Migration

New function components can accept ref directly as a prop. Existing forwardRef components can continue to work, and React provides a codemod for migration.

Ref callbacks can return cleanup functions

featuremedium impact

React 19 supports returning a cleanup function from a ref callback. React calls the cleanup when the element is removed.

Beforetsx
<div
  ref={(node) => {
    if (node) {
      setup(node);
    }
  }}
/>
Aftertsx
<div
  ref={(node) => {
    setup(node);

    return () => {
      cleanup(node);
    };
  }}
/>

Context can be rendered directly as a provider

featurelow impact

React 19 allows <Context> to be used directly as a provider instead of requiring <Context.Provider>.

Beforetsx
<ThemeContext.Provider value="dark">
  <App />
</ThemeContext.Provider>
Aftertsx
<ThemeContext value="dark">
  <App />
</ThemeContext>

Actions manage async mutations and pending state

featurehigh impact

React 19 introduces Actions for handling asynchronous transitions, including pending states, optimistic updates, errors, and form mutations.

Beforetsx
async function submit() {
  setPending(true);

  try {
    await updateName(name);
  } finally {
    setPending(false);
  }
}
Aftertsx
const [
  isPending,
  startTransition,
] = useTransition();

function submit() {
  startTransition(async () => {
    await updateName(name);
  });
}
Official sources

useActionState manages Action results and pending state

featuremedium impact

React 19 introduces useActionState for updating state based on the result of an Action while exposing pending state.

Aftertsx
import {
  useActionState,
} from "react";

const [
  error,
  submitAction,
  isPending,
] = useActionState(
  async (
    previousState,
    formData,
  ) => {
    const error =
      await updateName(
        formData.get("name"),
      );

    return error;
  },
  null,
);

useOptimistic supports optimistic UI updates

featuremedium impact

React 19 introduces useOptimistic for temporarily showing an expected result while an asynchronous Action is in progress.

Aftertsx
import {
  useOptimistic,
} from "react";

const [
  optimisticName,
  setOptimisticName,
] = useOptimistic(name);

async function submit(
  formData: FormData,
) {
  const nextName =
    String(formData.get("name"));

  setOptimisticName(nextName);

  await updateName(nextName);
}

The use API reads resources during render

featurehigh impact

React 19 introduces use for reading resources such as promises and context during rendering. Unlike Hooks, use can be called conditionally.

Aftertsx
import {
  use,
} from "react";

function Comments({
  commentsPromise,
}: {
  commentsPromise: Promise<Comment[]>;
}) {
  const comments =
    use(commentsPromise);

  return comments.map(
    (comment) => (
      <Comment
        key={comment.id}
        comment={comment}
      />
    ),
  );
}

Forms can invoke Actions directly

featurehigh impact

React 19 extends form action, input formAction, and button formAction props so they can receive functions that handle form submissions.

Beforetsx
<form
  onSubmit={handleSubmit}
>
  <input name="name" />

  <button type="submit">
    Save
  </button>
</form>
Aftertsx
async function saveName(
  formData: FormData,
) {
  await updateName(
    formData.get("name"),
  );
}

<form action={saveName}>
  <input name="name" />

  <button type="submit">
    Save
  </button>
</form>
Official sources

useFormStatus exposes parent form status

featuremedium impact

React 19 adds useFormStatus in react-dom so components can read information about the parent form submission, including whether it is pending.

Aftertsx
import {
  useFormStatus,
} from "react-dom";

function SubmitButton() {
  const {
    pending,
  } = useFormStatus();

  return (
    <button
      disabled={pending}
      type="submit"
    >
      {pending
        ? "Saving..."
        : "Save"}
    </button>
  );
}

Metadata tags can be rendered from components

featuremedium impact

React 19 adds native support for title, meta, and link metadata tags rendered from components and hoists them to the document head.

Aftertsx
function ProductPage({
  product,
}: {
  product: Product;
}) {
  return (
    <>
      <title>
        {product.name}
      </title>

      <meta
        name="description"
        content={
          product.description
        }
      />

      <ProductDetails
        product={product}
      />
    </>
  );
}

Stylesheets support precedence and rendering coordination

featuremedium impact

React 19 adds built-in support for stylesheet links with precedence, allowing React to coordinate stylesheet loading with rendering.

Aftertsx
<link
  rel="stylesheet"
  href="/styles.css"
  precedence="default"
/>

Async scripts can be rendered from components

featurelow impact

React 19 improves support for async scripts rendered anywhere in the component tree by deduplicating and managing them as document resources.

Aftertsx
<script
  async
  src="https://example.com/sdk.js"
/>

TypeScript

Implicit ref callback returns can cause TypeScript errors

Action required
typescriptmedium impact

Because React 19 ref callbacks may return cleanup functions, TypeScript no longer accepts some implicit returns that return the assigned value instead of void.

Beforetsx
<div
  ref={(current) =>
    (instance = current)
  }
/>
Aftertsx
<div
  ref={(current) => {
    instance = current;
  }}
/>
Migration

Change ref callback assignments that implicitly return a value into block-bodied callbacks that return nothing.

Performance

Resource preloading APIs are available

performancemedium impact

React 19 adds APIs for prefetching DNS, preconnecting to origins, preloading resources, and preinitializing scripts and stylesheets.

Aftertsx
import {
  preconnect,
  preload,
} from "react-dom";

preconnect(
  "https://cdn.example.com",
);

preload(
  "/fonts/inter.woff2",
  {
    as: "font",
  },
);

Tooling

Hydration errors provide improved diagnostics

toolinglow impact

React 19 improves hydration mismatch reporting by combining related messages and providing more useful information about the mismatch.

Behavior Changes

Render errors are no longer re-thrown

Action required
behaviormedium impact

React 19 changes render error handling. Uncaught errors are reported to window.reportError and errors caught by Error Boundaries are reported to console.error instead of being caught and re-thrown.

Beforetsx
const root = createRoot(container);

root.render(<App />);
Aftertsx
const root = createRoot(container, {
  onUncaughtError(error, errorInfo) {
    reportError(error, errorInfo);
  },

  onCaughtError(error, errorInfo) {
    reportCaughtError(
      error,
      errorInfo,
    );
  },
});

root.render(<App />);
Migration

If your error reporting relies on React re-throwing render errors, migrate to the onUncaughtError and onCaughtError options available on createRoot and hydrateRoot.

Deprecations

element.ref is deprecated

Action required
deprecationmedium impact

React 19 treats ref as a regular prop and deprecates accessing element.ref directly.

Beforetsx
const element = <Input ref={inputRef} />;

const ref = element.ref;
Aftertsx
const element = <Input ref={inputRef} />;

const ref = element.props.ref;
Migration

Read ref from element.props.ref instead of element.ref. Direct element.ref access will be removed from the JSX Element type in a future release.