If you’re using React 17, there’s a good chance that you’re not yet ready to upgrade to React 18. In this article, we’ll take a look at how to upgrade from React 17 to React 18 without any pain. First, let’s take a look at what’s new in React 18: React 16 support is gone, replaced by the newer and more powerful React Native. This means that you won’t be able to use legacy code that relies on 16-bit compatibility. If you need to use 16-bit code, then you’ll need to migrate it over to React Native. React 17 also doesn’t have any built-in support for webpack or Babel. These are important tools that are used by many developers and will be lost without them. You’ll need to install them separately if you want to upgrade. Once you’ve installed all of these tools, it’s time for the hard part: upgrading your code! We’ll start by upgrading our app from React 17 to 18 using the command line tool npm . This will take a little bit of time so be patient! Once it’s done, we can move on to upgrading our components and libraries!


React 18 evolves the popular JavaScript component framework with new features built around concurrent rendering and suspense. It promises better performance, more capabilities, and an improved developer experience for apps that make the switch.

In this article, we’ll show you how to upgrade your existing codebases to React 18. Bear in mind that this guide is only an overview of the most broadly applicable changes. Migration should be fairly painless for small projects already following React best practices; large sets of complex components may throw up some issues, which we’ll detail below.

Installing React 18

Before doing anything else, use npm to upgrade your project’s React dependency to v18:

The new release doesn’t technically have any backwards incompatibilities. The new features are activated on an opt-in basis. As you’ve not changed any code yet, you should be able to start your app and observe it rendering correctly. Your project will run with its existing React 17 behavior.

Enabling React 18 Features: The New Root API

Using React 18 without any codebase changes will cause one side effect: you’ll see a browser console warning each time your app mounts in development mode.

This deprecation message can be safely ignored if you’re not ready to upgrade your project. When you want to adopt React 18 capabilities, you need to make the change it describes. The old ReactDOM.render() function has been replaced with a new root API that’s more object-oriented. Besides improved ease-of-use, it also activates the concurrent rendering system that powers all the new headline features.

Within your index.js or app.js file, look for the lines that are similar to these:

This is a typical entrypoint for a React application. It renders an instance of the imported App component as your app’s root element. The rendered content is deposited as the innerHTML of the HTML element with id=“react”.

To switch to the React 18 root API, replace the code above with the following:

This has an equivalent effect to the old ReactDOM.render() API. Instead of initializing a root element and rendering your app as a single imperative operation, React 18 makes you create a root object first and then explicitly render your content.

Next look for any places in your code where you unmount your root node. Change ReactDOM.unmountComponentAtNode() to the new unmount() method on your root object:

Replacing Render Callbacks

The ReactDOM.render() method’s optional callback argument has no direct counterpart in the React 18 root API. You could previously use this code to log Rendered! to the console after React has finished rendering the root node:

This functionality was removed because the timing of the callback invocation is unpredictable when using React 18’s new partial hydration and streaming server rendering features. If you’re already using render callbacks and need to maintain compatibility, you can achieve similar behavior using the refs mechanism:

React calls function refs when components mount. Setting a ref on the component that’s your root node lets you detect when rendering occurs, providing a similar effect to the old render callback system.

Debugging Upgrade Problems

Your app should now be rendering using React 18 features and without any console warnings. Test your app thoroughly to make sure everything still works as you expect. If you find problems, you might be able to resolve them with these common resolutions.

Check for

Apps wrapped in the component may behave differently when rendering in React 18’s development mode. This is because Strict Mode now tests whether your codebase supports reusable state, a concept that will be fully introduced to React in a future release.

Reusable state allows React to remount a previously removed component with its last state automatically restored. This requires your components be resilient to double invocation of effects. Strict Mode now helps you prepare for reusable state by simulating mounting, unmounting, and remounting your components each time they’re used, surfacing any problems where the previous state can’t be restored. You can disable Strict Mode if it finds issues in your app or its dependencies that you’re not ready to address.

Support State Update Batching

React 18 changes how state updates are “batched” to improve performance. When you change state values multiple times in a function, React tries to combine them into a single re-render:

This mechanism increases efficiency but previously only worked inside React event handlers. With React 18, it works with all state updates, even if they originate from native event handlers, timeouts, or Promises. Some code might behave differently to before if you make consecutive state updates in any of these places.

You can disable this behavior in situations where you’re not ready to refactor your code. Wrap state updates in flushSync() to force them to commit immediately:

Stop Using Removed and Unsupported Features

Once all the above aspects have been addressed, your app should be fully compatible with React 18. Although there are a few more API surface changes, these shouldn’t impact the majority of apps. Here are some to be aware of:

unstable_changedBits has been removed – This unsupported API allowed opting out of context updates. It is no longer available. The Object. assign() polyfill has been removed – You should manually add the object-assign polyfill package if you need to support very old browsers without a built-in Object. assign(). Internet Explorer is no longer supported – React has officially dropped compatibility with Internet Explorer ahead of the browser’s end of support in June. You should not upgrade to React 18 if you still require your app to run in IE. Using Suspense with an undefined fallback is now equivalent to null – Suspense boundaries with fallback={undefined} were previously skipped, allowing code to cascade to the next parent boundary in the tree. React 18 now respects Suspense components without a fallback.

Server Side Rendering

Apps that use server side rendering will require a few more changes to work with React 18.

Inline with the new root API, you must replace the old hydrate() function in your client-side code with the new hydrateRoot() provided by the react-dom/client package:

In your server side code, replace deprecated rendering API calls with their new counterparts. In most cases, you should change renderToNodeStream() to the new renderToReadableStream(). The new stream APIs unlock access to React 18’s streaming server rendering capabilities, where the server can keep delivering new HTML to the browser after your app’s initial render.

Start Using React 18 Features

Now that you’ve upgraded you can start making your app more powerful by incorporating React 18 features. React’s use of concurrency means component renders can be interrupted, unlocking new capabilities and more responsive UIs.

Some of the added features include major updates to Suspense, a way to designate the priority of state updates with Transitions, and a built-in mechanism for throttling re-renders caused by non-urgent but high-frequency updates. There are several miscellaneous changes and improvements too: you can return undefined from a component’s render() method, the warning about calling setState() on unmounted components has been removed, and several new HTML attributes such as imageSizes, imageSrcSet, and aria-description are recognized by React DOM’s renderer.

Summary

React 18 is stable and ready to use. In most cases the upgrade process should be quick and easy, requiring only an npm update and a switch to the new root API. You should still test all your components though: they may behave differently in some situations, such as in Strict Mode or when automatic batching applies.

This new release points to the future direction of React as a high-performance framework for all kinds of web applications. It also extends React’s server-side rendering capabilities, adding Suspense on the server and the ability to keep streaming content to your users after the initial render. This gives developers more flexibility to distribute rendering across both the client and server.