--- title: "How to Upgrade to React 18" author: [rickhanlonii] --- As we shared in the [release post](/blog/2022/03/29/react-v18.html), React 18 introduces features powered by our new concurrent renderer, with a gradual adoption strategy for existing applications. In this post, we will guide you through the steps for upgrading to React 18. Please [report any issues](https://github.com/facebook/react/issues/new/choose) you encounter while upgrading to React 18. *Note for React Native users: React 18 will ship in a future version of React Native. This is because React 18 relies on the New React Native Architecture to benefit from the new capabilities presented in this blogpost. For more information, see the [React Conf keynote here](https://www.youtube.com/watch?v=FZ0cG47msEk&t=1530s).* ## Installing {#installing} To install the latest version of React: ```bash npm install react react-dom ``` Or if you’re using yarn: ```bash yarn add react react-dom ``` ## Updates to Client Rendering APIs {#updates-to-client-rendering-apis} When you first install React 18, you will see a warning in the console: > ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot React 18 introduces a new root API which provides better ergonomics for managing roots. The new root API also enables the new concurrent renderer, which allows you to opt-into concurrent features. ```js // Before import { render } from 'react-dom'; const container = document.getElementById('app'); render(, container); // After import { createRoot } from 'react-dom/client'; const container = document.getElementById('app'); const root = createRoot(container); // createRoot(container!) if you use TypeScript root.render(); ``` We’ve also changed `unmountComponentAtNode` to `root.unmount`: ```js // Before unmountComponentAtNode(container); // After root.unmount(); ``` We've also removed the callback from render, since it usually does not have the expected result when using Suspense: ```js // Before const container = document.getElementById('app'); render(, container, () => { console.log('rendered'); }); // After function AppWithCallbackAfterRender() { useEffect(() => { console.log('rendered'); }); return } const container = document.getElementById('app'); const root = createRoot(container); root.render(); ``` > Note: > > There is no one-to-one replacement for the old render callback API — it depends on your use case. See the working group post for [Replacing render with createRoot](https://github.com/reactwg/react-18/discussions/5) for more information. Finally, if your app uses server-side rendering with hydration, upgrade `hydrate` to `hydrateRoot`: ```js // Before import { hydrate } from 'react-dom'; const container = document.getElementById('app'); hydrate(, container); // After import { hydrateRoot } from 'react-dom/client'; const container = document.getElementById('app'); const root = hydrateRoot(container, ); // Unlike with createRoot, you don't need a separate root.render() call here. ``` For more information, see the [working group discussion here](https://github.com/reactwg/react-18/discussions/5). > Note > > **If your app doesn't work after upgrading, check whether it's wrapped in ``.** [Strict Mode has gotten stricter in React 18](#updates-to-strict-mode), and not all your components may be resilient to the new checks it adds in development mode. If removing Strict Mode fixes your app, you can remove it during the upgrade, and then add it back (either at the top or for a part of the tree) after you fix the issues that it's pointing out. ## Updates to Server Rendering APIs {#updates-to-server-rendering-apis} In this release, we’re revamping our `react-dom/server` APIs to fully support Suspense on the server and Streaming SSR. As part of these changes, we're deprecating the old Node streaming API, which does not support incremental Suspense streaming on the server. Using this API will now warn: * `renderToNodeStream`: **Deprecated ⛔️️** Instead, for streaming in Node environments, use: * `renderToPipeableStream`: **New ✨** We're also introducing a new API to support streaming SSR with Suspense for modern edge runtime environments, such as Deno and Cloudflare workers: * `renderToReadableStream`: **New ✨** The following APIs will continue working, but with limited support for Suspense: * `renderToString`: **Limited** ⚠️ * `renderToStaticMarkup`: **Limited** ⚠️ Finally, this API will continue to work for rendering e-mails: * `renderToStaticNodeStream` For more information on the changes to server rendering APIs, see the working group post on [Upgrading to React 18 on the server](https://github.com/reactwg/react-18/discussions/22), a [deep dive on the new Suspense SSR Architecture](https://github.com/reactwg/react-18/discussions/37), and [Shaundai Person’s](https://twitter.com/shaundai) talk on [Streaming Server Rendering with Suspense](https://www.youtube.com/watch?v=pj5N-Khihgc) at React Conf 2021. ## Updates to TypeScript definitions If your project uses TypeScript, you will need to update your `@types/react` and `@types/react-dom` dependencies to the latest versions. The new types are safer and catch issues that used to be ignored by the type checker. The most notable change is that the `children` prop now needs to be listed explicitly when defining props, for example: ```typescript{3} interface MyButtonProps { color: string; children?: React.ReactNode; } ``` See the [React 18 typings pull request](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/56210) for a full list of type-only changes. It links to example fixes in library types so you can see how to adjust your code. You can use the [automated migration script](https://github.com/eps1lon/types-react-codemod) to help port your application code to the new and safer typings faster. If you find a bug in the typings, please [file an issue](https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/new?category=issues-with-a-types-package) in the DefinitelyTyped repo. ## Automatic Batching {#automatic-batching} React 18 adds out-of-the-box performance improvements by doing more batching by default. Batching is when React groups multiple state updates into a single re-render for better performance. Before React 18, we only batched updates inside React event handlers. Updates inside of promises, setTimeout, native event handlers, or any other event were not batched in React by default: ```js // Before React 18 only React events were batched function handleClick() { setCount(c => c + 1); setFlag(f => !f); // React will only re-render once at the end (that's batching!) } setTimeout(() => { setCount(c => c + 1); setFlag(f => !f); // React will render twice, once for each state update (no batching) }, 1000); ``` Starting in React 18 with `createRoot`, all updates will be automatically batched, no matter where they originate from. This means that updates inside of timeouts, promises, native event handlers or any other event will batch the same way as updates inside of React events: ```js // After React 18 updates inside of timeouts, promises, // native event handlers or any other event are batched. function handleClick() { setCount(c => c + 1); setFlag(f => !f); // React will only re-render once at the end (that's batching!) } setTimeout(() => { setCount(c => c + 1); setFlag(f => !f); // React will only re-render once at the end (that's batching!) }, 1000); ``` This is a breaking change, but we expect this to result in less work rendering, and therefore better performance in your applications. To opt-out of automatic batching, you can use `flushSync`: ```js import { flushSync } from 'react-dom'; function handleClick() { flushSync(() => { setCounter(c => c + 1); }); // React has updated the DOM by now flushSync(() => { setFlag(f => !f); }); // React has updated the DOM by now } ``` For more information, see the [Automatic batching deep dive](https://github.com/reactwg/react-18/discussions/21). ## New APIs for Libraries {#new-apis-for-libraries} In the React 18 Working Group we worked with library maintainers to create new APIs needed to support concurrent rendering for use cases specific to their use case in areas like styles, and external stores. To support React 18, some libraries may need to switch to one of the following APIs: * `useSyncExternalStore` is a new hook that allows external stores to support concurrent reads by forcing updates to the store to be synchronous. This new API is recommended for any library that integrates with state external to React. For more information, see the [useSyncExternalStore overview post](https://github.com/reactwg/react-18/discussions/70) and [useSyncExternalStore API details](https://github.com/reactwg/react-18/discussions/86). * `useInsertionEffect` is a new hook that allows CSS-in-JS libraries to address performance issues of injecting styles in render. Unless you've already built a CSS-in-JS library we don't expect you to ever use this. This hook will run after the DOM is mutated, but before layout effects read the new layout. This solves an issue that already exists in React 17 and below, but is even more important in React 18 because React yields to the browser during concurrent rendering, giving it a chance to recalculate layout. For more information, see the [Library Upgrade Guide for `