This blog site has been archived. Go to react.dev/blog to see the recent posts. React 18 was years in the making, and with it brought valuable lessons for the React team. Its release was the result of many years of research and exploring many paths. Some of those paths were...
This blog site has been archived. Go to react.dev/blog to see the recent posts.
React 18 was years in the making, and with it brought valuable lessons for the React team. Its release was the result of many years of research and exploring many paths. Some of those paths were successful; many more were dead-ends that led to new insights. One lesson we’ve learned is that it’s frustrating for the community to wait for new features without having insight into these paths that we’re exploring.
We typically have a number of projects being worked on at any time, ranging from the more experimental to the clearly defined. Looking ahead, we’d like to start regularly sharing more about what we’ve been working on with the community across these projects.
To set expectations, this is not a roadmap with clear timelines. Many of these projects are under active research and are difficult to put concrete ship dates on. They may possibly never even ship in their current iteration depending on what we learn. Instead, we want to share with you the problem spaces we’re actively thinking about, and what we’ve learned so far.
Server Components
We announced an experimental demo of React Server Components (RSC) in December 2020. Since then we’ve been finishing up its dependencies in React 18, and working on changes inspired by experimental feedback.
In particular, we’re abandoning the idea of having forked I/O libraries (eg react-fetch), and instead adopting an async/await model for better compatibility. This doesn’t technically block RSC’s release because you can also use routers for data fetching. Another change is that we’re also moving away from the file extension approach in favor of annotating boundaries.
We’re working together with Vercel and Shopify to unify bundler support for shared semantics in both Webpack and Vite. Before launch, we want to make sure that the semantics of RSCs are the same across the whole React ecosystem. This is the major blocker for reaching stable.
Asset Loading
Currently, assets like scripts, external styles, fonts, and images are typically preloaded and loaded using external systems. This can make it tricky to coordinate across new environments like streaming, server components, and more.
We’re looking at adding APIs to preload and load deduplicated external assets through React APIs that work in all React environments.
We’re also looking at having these support Suspense so you can have images, CSS, and fonts that block display until they’re loaded but don’t block streaming and concurrent rendering. This can help avoid “popcorning“ as the visuals pop and layout shifts.
Static Server Rendering Optimizations
Static Site Generation (SSG) and Incremental Static Regeneration (ISR) are great ways to get performance for cacheable pages, but we think we can add features to improve performance of dynamic Server Side Rendering (SSR) – especially when most but not all of the content is cacheable. We’re exploring ways to optimize server rendering utilizing compilation and static passes.
React Optimizing Compiler
We gave an early preview of React Forget at React Conf 2021. It’s a compiler that automatically generates the equivalent of useMemo and useCallback calls to minimize the cost of re-rendering, while retaining React’s programming model.
Recently, we finished a rewrite of the compiler to make it more reliable and capable. This new architecture allows us to analyze and memoize more complex patterns such as the use of local mutations, and opens up many new compile-time optimization opportunities beyond just being on par with memoization hooks.
We’re also working on a playground for exploring many aspects of the compiler. While the goal of the playground is to make development of the compiler easier, we think that it will make it easier to try it out and build intuition for what the compiler does. It reveals various insights into how it works under the hood, and live renders the compiler’s outputs as you type. This will be shipped together with the compiler when it’s released.
Offscreen
Today, if you want to hide and show a component, you have two options. One is to add or remove it from the tree completely. The problem with this approach is that the state of your UI is lost each time you unmount, including state stored in the DOM, like scroll position.
The other option is to keep the component mounted and toggle the appearance visually using CSS. This preserves the state of your UI, but it comes at a performance cost, because React must keep rendering the hidden component and all of its children whenever it receives new updates.
Offscreen introduces a third option: hide the UI visually, but deprioritize its content. The idea is similar in spirit to the content-visibility CSS property: when content is hidden, it doesn’t need to stay in sync with the rest of the UI. React can defer the rendering work until the rest of the app is idle, or until the content becomes visible again.
Offscreen is a low level capability that unlocks high level features. Similar to React’s other concurrent features like startTransition, in most cases you won’t interact with the Offscreen API directly, but instead via an opinionated framework to implement patterns like:
Instant transitions. Some routing frameworks already prefetch data to speed up subsequent navigations, like when hovering over a link. With Offscreen, they’ll also be able to prerender the next screen in the background.
Reusable state. Similarly, when navigating between routes or tabs, you can use Offscreen to preserve the state of the previous screen so you can switch back and pick up where you left off.
Virtualized list rendering. When displaying large lists of items, virtualized list frameworks will prerender more rows than are currently visible. You can use Offscreen to prerender the hidden rows at a lower priority than the visible items in the list.
Backgrounded content. We’re also exploring a related feature for deprioritizing content in the background without hiding it, like when displaying a modal overlay.
Transition Tracing
Currently, React has two profiling tools. The original Profiler shows an overview of all the commits in a profiling session. For each commit, it also shows all components that rendered and the amount of time it took for them to render. We also have a beta version of a Timeline Profiler introduced in React 18 that shows when components schedule updates and when React works on these updates. Both of these profilers help developers identify performance problems in their code.
We’ve realized that developers don’t find knowing about individual slow commits or components out of context that useful. It’s more useful to know about what actually causes the slow commits. And that developers want to be able to track specific interactions (eg a button click, an initial load, or a page navigation) to watch for performance regressions and to understand why an interaction was slow and how to fix it.
We previously tried to solve this issue by creating an Interaction Tracing API, but it had some fundamental design flaws that reduced the accuracy of tracking why an interaction was slow and sometimes resulted in interactions never ending. We ended up removing this API because of these issues.
We are working on a new version for the Interaction Tracing API (tentatively called Transition Tracing because it is initiated via startTransition) that solves these problems.
New React Docs
Last year, we announced the beta version of the new React documentation website. The new learning materials teach Hooks first and has new diagrams, illustrations, as well as many interactive examples and challenges. We took a break from that work to focus on the React 18 release, but now that React 18 is out, we’re actively working to finish and ship the new documentation.
We are currently writing a detailed section about effects, as we’ve heard that is one of the more challenging topics for both new and experienced React users. Synchronizing with Effects is the first published page in the series, and there are more to come in the following weeks. When we first started writing a detailed section about effects, we’ve realized that many common effect patterns can be simplified by adding a new primitive to React. We’ve shared some initial thoughts on that in the useEvent RFC. It is currently in early research, and we are still iterating on the idea. We appreciate the community’s comments on the RFC so far, as well as the feedback and contributions to the ongoing documentation rewrite. We’d specifically like to thank Harish Kumar for submitting and reviewing many improvements to the new website implementation.
Thanks to Sophie Alpert for reviewing this blog post!
This blog site has been archived. Go to react.dev/blog to see the recent posts. React 18 is now available on npm! In our last post, we shared step-by-step instructions for upgrading your app to React 18. In this post, we’ll give an overview of what’s new in React 18, and what...
This blog site has been archived. Go to react.dev/blog to see the recent posts.
React 18 is now available on npm!
In our last post, we shared step-by-step instructions for upgrading your app to React 18. In this post, we’ll give an overview of what’s new in React 18, and what it means for the future.
Our latest major version includes out-of-the-box improvements like automatic batching, new APIs like startTransition, and streaming server-side rendering with support for Suspense.
Many of the features in React 18 are built on top of our new concurrent renderer, a behind-the-scenes change that unlocks powerful new capabilities. Concurrent React is opt-in — it’s only enabled when you use a concurrent feature — but we think it will have a big impact on the way people build applications.
We’ve spent years researching and developing support for concurrency in React, and we’ve taken extra care to provide a gradual adoption path for existing users. Last summer, we formed the React 18 Working Group to gather feedback from experts in the community and ensure a smooth upgrade experience for the entire React ecosystem.
In case you missed it, we shared a lot of this vision at React Conf 2021:
In the keynote, we explain how React 18 fits into our mission to make it easy for developers to build great user experiences
Below is a full overview of what to expect in this release, starting with Concurrent Rendering.
Note for React Native users: React 18 will ship in React Native with the New React Native Architecture. For more information, see the React Conf keynote here.
What is Concurrent React?
The most important addition in React 18 is something we hope you never have to think about: concurrency. We think this is largely true for application developers, though the story may be a bit more complicated for library maintainers.
Concurrency is not a feature, per se. It’s a new behind-the-scenes mechanism that enables React to prepare multiple versions of your UI at the same time. You can think of concurrency as an implementation detail — it’s valuable because of the features that it unlocks. React uses sophisticated techniques in its internal implementation, like priority queues and multiple buffering. But you won’t see those concepts anywhere in our public APIs.
When we design APIs, we try to hide implementation details from developers. As a React developer, you focus on what you want the user experience to look like, and React handles how to deliver that experience. So we don’t expect React developers to know how concurrency works under the hood.
However, Concurrent React is more important than a typical implementation detail — it’s a foundational update to React’s core rendering model. So while it’s not super important to know how concurrency works, it may be worth knowing what it is at a high level.
A key property of Concurrent React is that rendering is interruptible. When you first upgrade to React 18, before adding any concurrent features, updates are rendered the same as in previous versions of React — in a single, uninterrupted, synchronous transaction. With synchronous rendering, once an update starts rendering, nothing can interrupt it until the user can see the result on screen.
In a concurrent render, this is not always the case. React may start rendering an update, pause in the middle, then continue later. It may even abandon an in-progress render altogether. React guarantees that the UI will appear consistent even if a render is interrupted. To do this, it waits to perform DOM mutations until the end, once the entire tree has been evaluated. With this capability, React can prepare new screens in the background without blocking the main thread. This means the UI can respond immediately to user input even if it’s in the middle of a large rendering task, creating a fluid user experience.
Another example is reusable state. Concurrent React can remove sections of the UI from the screen, then add them back later while reusing the previous state. For example, when a user tabs away from a screen and back, React should be able to restore the previous screen in the same state it was in before. In an upcoming minor, we’re planning to add a new component called <Offscreen> that implements this pattern. Similarly, you’ll be able to use Offscreen to prepare new UI in the background so that it’s ready before the user reveals it.
Concurrent rendering is a powerful new tool in React and most of our new features are built to take advantage of it, including Suspense, transitions, and streaming server rendering. But React 18 is just the beginning of what we aim to build on this new foundation.
Gradually Adopting Concurrent Features
Technically, concurrent rendering is a breaking change. Because concurrent rendering is interruptible, components behave slightly differently when it is enabled.
In our testing, we’ve upgraded thousands of components to React 18. What we’ve found is that nearly all existing components “just work” with concurrent rendering, without any changes. However, some of them may require some additional migration effort. Although the changes are usually small, you’ll still have the ability to make them at your own pace. The new rendering behavior in React 18 is only enabled in the parts of your app that use new features.
The overall upgrade strategy is to get your application running on React 18 without breaking existing code. Then you can gradually start adding concurrent features at your own pace. You can use <StrictMode> to help surface concurrency-related bugs during development. Strict Mode doesn’t affect production behavior, but during development it will log extra warnings and double-invoke functions that are expected to be idempotent. It won’t catch everything, but it’s effective at preventing the most common types of mistakes.
After you upgrade to React 18, you’ll be able to start using concurrent features immediately. For example, you can use startTransition to navigate between screens without blocking user input. Or useDeferredValue to throttle expensive re-renders.
However, long term, we expect the main way you’ll add concurrency to your app is by using a concurrent-enabled library or framework. In most cases, you won’t interact with concurrent APIs directly. For example, instead of developers calling startTransition whenever they navigate to a new screen, router libraries will automatically wrap navigations in startTransition.
It may take some time for libraries to upgrade to be concurrent compatible. We’ve provided new APIs to make it easier for libraries to take advantage of concurrent features. In the meantime, please be patient with maintainers as we work to gradually migrate the React ecosystem.
In React 18, you can start using Suspense for data fetching in opinionated frameworks like Relay, Next.js, Hydrogen, or Remix. Ad hoc data fetching with Suspense is technically possible, but still not recommended as a general strategy.
In the future, we may expose additional primitives that could make it easier to access your data with Suspense, perhaps without the use of an opinionated framework. However, Suspense works best when it’s deeply integrated into your application’s architecture: your router, your data layer, and your server rendering environment. So even long term, we expect that libraries and frameworks will play a crucial role in the React ecosystem.
As in previous versions of React, you can also use Suspense for code splitting on the client with React.lazy. But our vision for Suspense has always been about much more than loading code — the goal is to extend support for Suspense so that eventually, the same declarative Suspense fallback can handle any asynchronous operation (loading code, data, images, etc).
Server Components is Still in Development
Server Components is an upcoming feature that allows developers to build apps that span the server and client, combining the rich interactivity of client-side apps with the improved performance of traditional server rendering. Server Components is not inherently coupled to Concurrent React, but it’s designed to work best with concurrent features like Suspense and streaming server rendering.
Server Components is still experimental, but we expect to release an initial version in a minor 18.x release. In the meantime, we’re working with frameworks like Next.js, Hydrogen, and Remix to advance the proposal and get it ready for broad adoption.
What’s New in React 18
New Feature: Automatic Batching
Batching is when React groups multiple state updates into a single re-render for better performance. Without automatic batching, 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. With automatic batching, these updates will be batched automatically:
// Before: only React events were batched.setTimeout(()=>{setCount(c=> c +1);setFlag(f=>!f);// React will render twice, once for each state update (no batching)},1000);// After: updates inside of timeouts, promises,// native event handlers or any other event are batched.setTimeout(()=>{setCount(c=> c +1);setFlag(f=>!f);// React will only re-render once at the end (that's batching!)},1000);
A transition is a new concept in React to distinguish between urgent and non-urgent updates.
Urgent updates reflect direct interaction, like typing, clicking, pressing, and so on.
Transition updates transition the UI from one view to another.
Urgent updates like typing, clicking, or pressing, need immediate response to match our intuitions about how physical objects behave. Otherwise they feel “wrong”. However, transitions are different because the user doesn’t expect to see every intermediate value on screen.
For example, when you select a filter in a dropdown, you expect the filter button itself to respond immediately when you click. However, the actual results may transition separately. A small delay would be imperceptible and often expected. And if you change the filter again before the results are done rendering, you only care to see the latest results.
Typically, for the best user experience, a single user input should result in both an urgent update and a non-urgent one. You can use startTransition API inside an input event to inform React which updates are urgent and which are “transitions”:
import{startTransition}from'react';// Urgent: Show what was typedsetInputValue(input);// Mark any state updates inside as transitionsstartTransition(()=>{// Transition: Show the resultssetSearchQuery(input);});
Updates wrapped in startTransition are handled as non-urgent and will be interrupted if more urgent updates like clicks or key presses come in. If a transition gets interrupted by the user (for example, by typing multiple characters in a row), React will throw out the stale rendering work that wasn’t finished and render only the latest update.
useTransition: a hook to start transitions, including a value to track the pending state.
startTransition: a method to start transitions when the hook cannot be used.
Transitions will opt in to concurrent rendering, which allows the update to be interrupted. If the content re-suspends, transitions also tell React to continue showing the current content while rendering the transition content in the background (see the Suspense RFC for more info).
Suspense makes the “UI loading state” a first-class declarative concept in the React programming model. This lets us build higher-level features on top of it.
We introduced a limited version of Suspense several years ago. However, the only supported use case was code splitting with React.lazy, and it wasn’t supported at all when rendering on the server.
In React 18, we’ve added support for Suspense on the server and expanded its capabilities using concurrent rendering features.
Suspense in React 18 works best when combined with the transition API. If you suspend during a transition, React will prevent already-visible content from being replaced by a fallback. Instead, React will delay the render until enough data has loaded to prevent a bad loading state.
In this release we took the opportunity to redesign the APIs we expose for rendering on the client and server. These changes allow users to continue using the old APIs in React 17 mode while they upgrade to the new APIs in React 18.
React DOM Client
These new APIs are now exported from react-dom/client:
createRoot: New method to create a root to render or unmount. Use it instead of ReactDOM.render. New features in React 18 don’t work without it.
hydrateRoot: New method to hydrate a server rendered application. Use it instead of ReactDOM.hydrate in conjunction with the new React DOM Server APIs. New features in React 18 don’t work without it.
Both createRoot and hydrateRoot accept a new option called onRecoverableError in case you want to be notified when React recovers from errors during rendering or hydration for logging. By default, React will use reportError, or console.error in the older browsers.
In the future, we’d like to add a feature that allows React to add and remove sections of the UI while preserving state. For example, when a user tabs away from a screen and back, React should be able to immediately show the previous screen. To do this, React would unmount and remount trees using the same component state as before.
This feature will give React apps better performance out-of-the-box, but requires components to be resilient to effects being mounted and destroyed multiple times. Most effects will work without any changes, but some effects assume they are only mounted or destroyed once.
To help surface these issues, React 18 introduces a new development-only check to Strict Mode. This new check will automatically unmount and remount every component, whenever a component mounts for the first time, restoring the previous state on the second mount.
Before this change, React would mount the component and create the effects:
* React mounts the component.
* Layout effects are created.
* Effects are created.
With Strict Mode in React 18, React will simulate unmounting and remounting the component in development mode:
* React mounts the component.
* Layout effects are created.
* Effects are created.
* React simulates unmounting the component.
* Layout effects are destroyed.
* Effects are destroyed.
* React simulates mounting the component with the previous state.
* Layout effects are created.
* Effects are created.
useId is a new hook for generating unique IDs on both the client and server, while avoiding hydration mismatches. It is primarily useful for component libraries integrating with accessibility APIs that require unique IDs. This solves an issue that already exists in React 17 and below, but it’s even more important in React 18 because of how the new streaming server renderer delivers HTML out-of-order. See docs here.
Note
useId is not for generating keys in a list. Keys should be generated from your data.
useTransition
useTransition and startTransition let you mark some state updates as not urgent. Other state updates are considered urgent by default. React will allow urgent state updates (for example, updating a text input) to interrupt non-urgent state updates (for example, rendering a list of search results). See docs here
useDeferredValue
useDeferredValue lets you defer re-rendering a non-urgent part of the tree. It is similar to debouncing, but has a few advantages compared to it. There is no fixed time delay, so React will attempt the deferred render right after the first render is reflected on the screen. The deferred render is interruptible and doesn’t block user input. See docs here.
useSyncExternalStore
useSyncExternalStore is a new hook that allows external stores to support concurrent reads by forcing updates to the store to be synchronous. It removes the need for useEffect when implementing subscriptions to external data sources, and is recommended for any library that integrates with state external to React. See docs here.
Note
useSyncExternalStore is intended to be used by libraries, not application code.
useInsertionEffect
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. See docs here.
Note
useInsertionEffect is intended to be used by libraries, not application code.
How to Upgrade
See How to Upgrade to React 18 for step-by-step instructions and a full list of breaking and notable changes.
This blog site has been archived. Go to react.dev/blog to see the recent posts. As we shared in the release post, 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...
This blog site has been archived. Go to react.dev/blog to see the recent posts.
As we shared in the release post, 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.
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.
Installing
To install the latest version of React:
npminstall react react-dom
Or if you’re using yarn:
yarnadd react react-dom
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.
// Beforeimport{ render }from'react-dom';const container = document.getElementById('app');render(<Apptab="home"/>, container);// Afterimport{ createRoot }from'react-dom/client';const container = document.getElementById('app');const root =createRoot(container);// createRoot(container!) if you use TypeScript
root.render(<Apptab="home"/>);
We’ve also changed unmountComponentAtNode to root.unmount:
// BeforeunmountComponentAtNode(container);// After
root.unmount();
We’ve also removed the callback from render, since it usually does not have the expected result when using Suspense:
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 for more information.
Finally, if your app uses server-side rendering with hydration, upgrade hydrate to hydrateRoot:
// Beforeimport{ hydrate }from'react-dom';const container = document.getElementById('app');hydrate(<Apptab="home"/>, container);// Afterimport{ hydrateRoot }from'react-dom/client';const container = document.getElementById('app');const root =hydrateRoot(container,<Apptab="home"/>);// Unlike with createRoot, you don't need a separate root.render() call here.
If your app doesn’t work after upgrading, check whether it’s wrapped in <StrictMode>.Strict Mode has gotten stricter in React 18, 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
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:
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:
See the React 18 typings pull request 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 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 in the DefinitelyTyped repo.
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:
// Before React 18 only React events were batchedfunctionhandleClick(){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:
// After React 18 updates inside of timeouts, promises,// native event handlers or any other event are batched.functionhandleClick(){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:
import{ flushSync }from'react-dom';functionhandleClick(){flushSync(()=>{setCounter(c=> c +1);});// React has updated the DOM by nowflushSync(()=>{setFlag(f=>!f);});// React has updated the DOM by now}
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 and useSyncExternalStore API details.
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 <style>.
React 18 also introduces new APIs for concurrent rendering such as startTransition, useDeferredValue and useId, which we share more about in the release post.
Updates to Strict Mode
In the future, we’d like to add a feature that allows React to add and remove sections of the UI while preserving state. For example, when a user tabs away from a screen and back, React should be able to immediately show the previous screen. To do this, React would unmount and remount trees using the same component state as before.
This feature will give React better performance out-of-the-box, but requires components to be resilient to effects being mounted and destroyed multiple times. Most effects will work without any changes, but some effects assume they are only mounted or destroyed once.
To help surface these issues, React 18 introduces a new development-only check to Strict Mode. This new check will automatically unmount and remount every component, whenever a component mounts for the first time, restoring the previous state on the second mount.
Before this change, React would mount the component and create the effects:
* React mounts the component.
* Layout effects are created.
* Effect effects are created.
With Strict Mode in React 18, React will simulate unmounting and remounting the component in development mode:
* React mounts the component.
* Layout effects are created.
* Effect effects are created.
* React simulates unmounting the component.
* Layout effects are destroyed.
* Effects are destroyed.
* React simulates mounting the component with the previous state.
* Layout effect setup code runs
* Effect setup code runs
When you first update your tests to use createRoot, you may see this warning in your test console:
The current testing environment is not configured to support act(…)
To fix this, set globalThis.IS_REACT_ACT_ENVIRONMENT to true before running your test:
// In your test setup file
globalThis.IS_REACT_ACT_ENVIRONMENT=true;
The purpose of the flag is to tell React that it’s running in a unit test-like environment. React will log helpful warnings if you forget to wrap an update with act.
You can also set the flag to false to tell React that act isn’t needed. This can be useful for end-to-end tests that simulate a full browser environment.
In this release, React is dropping support for Internet Explorer, which is going out of support on June 15, 2022. We’re making this change now because new features introduced in React 18 are built using modern browser features such as microtasks which cannot be adequately polyfilled in IE.
If you need to support Internet Explorer we recommend you stay with React 17.
Deprecations
react-dom: ReactDOM.render has been deprecated. Using it will warn and run your app in React 17 mode.
react-dom: ReactDOM.hydrate has been deprecated. Using it will warn and run your app in React 17 mode.
react-dom: ReactDOM.unmountComponentAtNode has been deprecated.
react-dom: ReactDOM.renderSubtreeIntoContainer has been deprecated.
react-dom/server: ReactDOMServer.renderToNodeStream has been deprecated.
Other Breaking Changes
Consistent useEffect timing: React now always synchronously flushes effect functions if the update was triggered during a discrete user input event such as a click or a keydown event. Previously, the behavior wasn’t always predictable or consistent.
Stricter hydration errors: Hydration mismatches due to missing or extra text content are now treated like errors instead of warnings. React will no longer attempt to “patch up” individual nodes by inserting or deleting a node on the client in an attempt to match the server markup, and will revert to client rendering up to the closest <Suspense> boundary in the tree. This ensures the hydrated tree is consistent and avoids potential privacy and security holes that can be caused by hydration mismatches.
Suspense trees are always consistent: If a component suspends before it’s fully added to the tree, React will not add it to the tree in an incomplete state or fire its effects. Instead, React will throw away the new tree completely, wait for the asynchronous operation to finish, and then retry rendering again from scratch. React will render the retry attempt concurrently, and without blocking the browser.
Layout Effects with Suspense: When a tree re-suspends and reverts to a fallback, React will now clean up layout effects, and then re-create them when the content inside the boundary is shown again. This fixes an issue which prevented component libraries from correctly measuring layout when used with Suspense.
New JS Environment Requirements: React now depends on modern browsers features including Promise, Symbol, and Object.assign. If you support older browsers and devices such as Internet Explorer which do not provide modern browser features natively or have non-compliant implementations, consider including a global polyfill in your bundled application.
Other Notable Changes
React
Components can now render undefined: React no longer warns if you return undefined from a component. This makes the allowed component return values consistent with values that are allowed in the middle of a component tree. We suggest to use a linter to prevent mistakes like forgetting a return statement before JSX.
In tests, act warnings are now opt-in: If you’re running end-to-end tests, the act warnings are unnecessary. We’ve introduced an opt-in mechanism so you can enable them only for unit tests where they are useful and beneficial.
No warning about setState on unmounted components: Previously, React warned about memory leaks when you call setState on an unmounted component. This warning was added for subscriptions, but people primarily run into it in scenarios where setting state is fine, and workarounds make the code worse. We’ve removed this warning.
No suppression of console logs: When you use Strict Mode, React renders each component twice to help you find unexpected side effects. In React 17, we’ve suppressed console logs for one of the two renders to make the logs easier to read. In response to community feedback about this being confusing, we’ve removed the suppression. Instead, if you have React DevTools installed, the second log’s renders will be displayed in grey, and there will be an option (off by default) to suppress them completely.
Improved memory usage: React now cleans up more internal fields on unmount, making the impact from unfixed memory leaks that may exist in your application code less severe.
React DOM Server
renderToString: Will no longer error when suspending on the server. Instead, it will emit the fallback HTML for the closest <Suspense> boundary and then retry rendering the same content on the client. It is still recommended that you switch to a streaming API like renderToPipeableStream or renderToReadableStream instead.
renderToStaticMarkup: Will no longer error when suspending on the server. Instead, it will emit the fallback HTML for the closest <Suspense> boundary.
This blog site has been archived. Go to react.dev/blog to see the recent posts. Last week we hosted our 6th React Conf. In previous years, we’ve used the React Conf stage to deliver industry changing announcements such as React Native and React Hooks. This year, we shared our...
This blog site has been archived. Go to react.dev/blog to see the recent posts.
Last week we hosted our 6th React Conf. In previous years, we’ve used the React Conf stage to deliver industry changing announcements such as React Native and React Hooks. This year, we shared our multi-platform vision for React, starting with the release of React 18 and gradual adoption of concurrent features.
This was the first time React Conf was hosted online, and it was streamed for free, translated to 8 different languages. Participants from all over the world joined our conference Discord and the replay event for accessibility in all timezones. Over 50,000 people registered, with over 60,000 views of 19 talks, and 5,000 participants in Discord across both events.
In the keynote, we shared our vision for the future of React starting with React 18.
React 18 adds the long-awaited concurrent renderer and updates to Suspense without any major breaking changes. Apps can upgrade to React 18 and begin gradually adopting concurrent features with the amount of effort on par with any other major release.
This means there is no concurrent mode, only concurrent features.
In the keynote, we also shared our vision for Suspense, Server Components, new React working groups, and our long-term many-platform vision for React Native.
In the keynote, we also announced that the React 18 RC is available to try now. Pending further feedback, this is the exact version of React that we will publish to stable early next year.
To try the React 18 RC, upgrade your dependencies:
For a demo of upgrading to React 18, see Shruti Kapoor’s talk here:
Streaming Server Rendering with Suspense
React 18 also includes improvements to server-side rendering performance using Suspense.
Streaming server rendering lets you generate HTML from React components on the server, and stream that HTML to your users. In React 18, you can use Suspense to break down your app into smaller independent units which can be streamed independently of each other without blocking the rest of the app. This means users will see your content sooner and be able to start interacting with it much faster.
For React 18, we created our first Working Group to collaborate with a panel of experts, developers, library maintainers, and educators. Together we worked to create our gradual adoption strategy and refine new APIs such as useId, useSyncExternalStore, and useInsertionEffect.
To support the new features in this release, we also announced the newly formed React DevTools team and a new Timeline Profiler to help developers debug their React apps.
For more information and a demo of new DevTools features, see Brian Vaughn’s talk:
React without memo
Looking further into the future, Xuan Huang (黄玄) shared an update from our React Labs research into an auto-memoizing compiler. Check out this talk for more information and a demo of the compiler prototype:
React docs keynote
Rachel Nabors kicked off a section of talks about learning and designing with React with a keynote about our investment in React’s new docs:
And more…
We also heard talks on learning and designing with React:
Thanks to Lauren Tan for setting up the conference Discord and serving as our Discord admin.
Thanks to Seth Webster for feedback on overall direction and making sure we were focused on diversity and inclusion.
Thanks to Rachel Nabors for spearheading our moderation effort, and Aisha Blake for creating our moderation guide, leading our moderation team, training the translators and moderators, and helping to moderate both events.
Thanks to Leah Silber for sharing her experience running conferences, learnings from running RustConf, and for her book Event Driven and the advice it contains for running conferences.
Thanks to Kevin Lewis and Rachel Nabors for sharing their experience running Women of React Conf.
Thanks to Dan Lebowitz for help designing and building the conference website and tickets.
Thanks to Laura Podolak Waddell, Desmond Osei-Acheampong, Mark Rossi, Josh Toberman and others on the Facebook Video Productions team for recording the videos for the Keynote and Meta employee talks.
Thanks to our partner HitPlay for helping to organize the conference, editing all the videos in the stream, translating all the talks, and moderating the Discord in multiple languages.
Finally, thanks to all of our participants for making this a great React Conf!
This blog site has been archived. Go to react.dev/blog to see the recent posts. Update Nov. 15th, 2021 React 18 is now in beta. More information about the status of the release is available in the React 18 Working Group post. The React team is excited to share a few updates:...
This blog site has been archived. Go to react.dev/blog to see the recent posts.
We’ve started work on the React 18 release, which will be our next major version.
We’ve created a Working Group to prepare the community for gradual adoption of new features in React 18.
We’ve published a React 18 Alpha so that library authors can try it and provide feedback.
These updates are primarily aimed at maintainers of third-party libraries. If you’re learning, teaching, or using React to build user-facing applications, you can safely ignore this post. But you are welcome to follow the discussions in the React 18 Working Group if you’re curious!
These features are possible thanks to a new opt-in mechanism we’re adding in React 18. It’s called “concurrent rendering” and it lets React prepare multiple versions of the UI at the same time. This change is mostly behind-the-scenes, but it unlocks new possibilities to improve both real and perceived performance of your app.
If you’ve been following our research into the future of React (we don’t expect you to!), you might have heard of something called “concurrent mode” or that it might break your app. In response to this feedback from the community, we’ve redesigned the upgrade strategy for gradual adoption. Instead of an all-or-nothing “mode”, concurrent rendering will only be enabled for updates triggered by one of the new features. In practice, this means you will be able to adopt React 18 without rewrites and try the new features at your own pace.
A gradual adoption strategy
Since concurrency in React 18 is opt-in, there are no significant out-of-the-box breaking changes to component behavior. You can upgrade to React 18 with minimal or no changes to your application code, with a level of effort comparable to a typical major React release. Based on our experience converting several apps to React 18, we expect that many users will be able to upgrade within a single afternoon.
We successfully shipped concurrent features to tens of thousands of components at Facebook, and in our experience, we’ve found that most React components “just work” without additional changes. We’re committed to making sure this is a smooth upgrade for the entire community, so today we’re announcing the React 18 Working Group.
Working with the community
We’re trying something new for this release: We’ve invited a panel of experts, developers, library authors, and educators from across the React community to participate in our React 18 Working Group to provide feedback, ask questions, and collaborate on the release. We couldn’t invite everyone we wanted to this initial, small group, but if this experiment works out, we hope there will be more in the future!
The goal of the React 18 Working Group is to prepare the ecosystem for a smooth, gradual adoption of React 18 by existing applications and libraries. The Working Group is hosted on GitHub Discussions and is available for the public to read. Members of the working group can leave feedback, ask questions, and share ideas. The core team will also use the discussions repo to share our research findings. As the stable release gets closer, any important information will also be posted on this blog.
For more information on upgrading to React 18, or additional resources about the release, see the React 18 announcement post.
Because we expect an initial surge of interest in the Working Group, only invited members will be allowed to create or comment on threads. However, the threads are fully visible to the public, so everyone has access to the same information. We believe this is a good compromise between creating a productive environment for working group members, while maintaining transparency with the wider community.
As always, you can submit bug reports, questions, and general feedback to our issue tracker.
How to try React 18 Alpha today
New alphas are regularly published to npm using the @alpha tag. These releases are built using the most recent commit to our main repo. When a feature or bugfix is merged, it will appear in an alpha the following weekday.
There may be significant behavioral or API changes between alpha releases. Please remember that alpha releases are not recommended for user-facing, production applications.
Projected React 18 release timeline
We don’t have a specific release date scheduled, but we expect it will take several months of feedback and iteration before React 18 is ready for most production applications.
Library Alpha: Available today
Public Beta: At least several months
Release Candidate (RC): At least several weeks after Beta
General Availability: At least several weeks after RC
More details about our projected release timeline are available in the Working Group. We’ll post updates on this blog when we’re closer to a public release.
This blog site has been archived. Go to react.dev/blog to see the recent posts. 2020 has been a long year. As it comes to an end we wanted to share a special Holiday Update on our research into zero-bundle-size React Server Components. To introduce React Server Components, we...
This blog site has been archived. Go to react.dev/blog to see the recent posts.
2020 has been a long year. As it comes to an end we wanted to share a special Holiday Update on our research into zero-bundle-size React Server Components.
To introduce React Server Components, we have prepared a talk and a demo. If you want, you can check them out during the holidays, or later when work picks back up in the new year.
React Server Components are still in research and development. We are sharing this work in the spirit of transparency and to get initial feedback from the React community. There will be plenty of time for that, so don’t feel like you have to catch up right now!
If you want to check them out, we recommend to go in the following order:
Watch the talk to learn about React Server Components and see the demo.
Clone the demo to play with React Server Components on your computer.
This blog site has been archived. Go to react.dev/blog to see the recent posts. Today, we are releasing React 17! We’ve written at length about the role of the React 17 release and the changes it contains in the React 17 RC blog post. This post is a brief summary of it, so if...
This blog site has been archived. Go to react.dev/blog to see the recent posts.
Today, we are releasing React 17! We’ve written at length about the role of the React 17 release and the changes it contains in the React 17 RC blog post. This post is a brief summary of it, so if you’ve already read the RC post, you can skip this one.
No New Features
The React 17 release is unusual because it doesn’t add any new developer-facing features. Instead, this release is primarily focused on making it easier to upgrade React itself.
In particular, React 17 is a “stepping stone” release that makes it safer to embed a tree managed by one version of React inside a tree managed by a different version of React.
It also makes it easier to embed React into apps built with other technologies.
Gradual Upgrades
React 17 enables gradual React upgrades. When you upgrade from React 15 to 16 (or, this time, from React 16 to 17), you would usually upgrade your whole app at once. This works well for many apps. But it can get increasingly challenging if the codebase was written more than a few years ago and isn’t actively maintained. And while it’s possible to use two versions of React on the page, until React 17 this has been fragile and caused problems with events.
We’re fixing many of those problems with React 17. This means that when React 18 and the next future versions come out, you will now have more options. The first option will be to upgrade your whole app at once, like you might have done before. But you will also have an option to upgrade your app piece by piece. For example, you might decide to migrate most of your app to React 18, but keep some lazy-loaded dialog or a subroute on React 17.
This doesn’t mean you have to do gradual upgrades. For most apps, upgrading all at once is still the best solution. Loading two versions of React — even if one of them is loaded lazily on demand — is still not ideal. However, for larger apps that aren’t actively maintained, this option makes sense to consider, and React 17 lets those apps not get left behind.
We’ve prepared an example repository demonstrating how to lazy-load an older version of React if necessary. This demo uses Create React App, but it should be possible to follow a similar approach with any other tool. We welcome demos using other tooling as pull requests.
Note
We’ve postponed other changes until after React 17. The goal of this release is to enable gradual upgrades. If upgrading to React 17 were too difficult, it would defeat its purpose.
Changes to Event Delegation
To enable gradual updates, we’ve needed to make some changes to the React event system. React 17 is a major release because these changes are potentially breaking. You can check out our versioning FAQ to learn more about our commitment to stability.
In React 17, React will no longer attach event handlers at the document level under the hood. Instead, it will attach them to the root DOM container into which your React tree is rendered:
In React 16 and earlier, React would do document.addEventListener() for most events. React 17 will call rootNode.addEventListener() under the hood instead.
We’ve only had to change fewer than twenty components out of 100,000+ in the Facebook product code to work with these changes, so we expect that most apps can upgrade to React 17 without too much trouble. Please tell us if you run into problems.
New JSX Transform
React 17 supports the new JSX transform. We’ve also backported support for it to React 16.14.0, React 15.7.0, and 0.14.10. Note that it is completely opt-in, and you don’t have to use it. The classic JSX transform will keep working, and there are no plans to stop supporting it.
React Native
React Native has a separate release schedule. We landed the support for React 17 in React Native 0.64. As always, you can track the release discussions on the React Native Community releases issue tracker.
This blog site has been archived. Go to react.dev/blog to see the recent posts. Although React 17 doesn’t contain new features, it will provide support for a new version of the JSX transform. In this post, we will describe what it is and how to try it. What’s a JSX Transform?...
This blog site has been archived. Go to react.dev/blog to see the recent posts.
Although React 17 doesn’t contain new features, it will provide support for a new version of the JSX transform. In this post, we will describe what it is and how to try it.
What’s a JSX Transform?
Browsers don’t understand JSX out of the box, so most React users rely on a compiler like Babel or TypeScript to transform JSX code into regular JavaScript. Many preconfigured toolkits like Create React App or Next.js also include a JSX transform under the hood.
Together with the React 17 release, we’ve wanted to make a few improvements to the JSX transform, but we didn’t want to break existing setups. This is why we worked with Babel to offer a new, rewritten version of the JSX transform for people who would like to upgrade.
Upgrading to the new transform is completely optional, but it has a few benefits:
With the new transform, you can use JSX without importing React.
Depending on your setup, its compiled output may slightly improve the bundle size.
It will enable future improvements that reduce the number of concepts you need to learn React.
This upgrade will not change the JSX syntax and is not required. The old JSX transform will keep working as usual, and there are no plans to remove the support for it.
React 17 RC already includes support for the new transform, so go give it a try! To make it easier to adopt, we’ve also backported its support to React 16.14.0, React 15.7.0, and React 0.14.10. You can find the upgrade instructions for different tools below.
Now let’s take a closer look at the differences between the old and the new transform.
What’s Different in the New Transform?
When you use JSX, the compiler transforms it into React function calls that the browser can understand. The old JSX transform turned JSX into React.createElement(...) calls.
For example, let’s say your source code looks like this:
Your source code doesn’t need to change in any way. We’re describing how the JSX transform turns your JSX source code into the JavaScript code a browser can understand.
However, this is not perfect:
Because JSX was compiled into React.createElement, React needed to be in scope if you used JSX.
To solve these issues, React 17 introduces two new entry points to the React package that are intended to only be used by compilers like Babel and TypeScript. Instead of transforming JSX to React.createElement, the new JSX transform automatically imports special functions from those new entry points in the React package and calls them.
Let’s say that your source code looks like this:
functionApp(){return<h1>Hello World</h1>;}
This is what the new JSX transform compiles it to:
// Inserted by a compiler (don't import it yourself!)import{jsx as _jsx}from'react/jsx-runtime';functionApp(){return_jsx('h1',{children:'Hello world'});}
Note how our original code did not need to import React to use JSX anymore! (But we would still need to import React in order to use Hooks or other exports that React provides.)
This change is fully compatible with all of the existing JSX code, so you won’t have to change your components. If you’re curious, you can check out the technical RFC for more details about how the new transform works.
Note
The functions inside react/jsx-runtime and react/jsx-dev-runtime must only be used by the compiler transform. If you need to manually create elements in your code, you should keep using React.createElement. It will continue to work and is not going away.
How to Upgrade to the New JSX Transform
If you aren’t ready to upgrade to the new JSX transform or if you are using JSX for another library, don’t worry. The old transform will not be removed and will continue to be supported.
If you want to upgrade, you will need two things:
A version of React that supports the new transform (React 17 RC and higher supports it, but we’ve also released React 16.14.0, React 15.7.0, and React 0.14.10 for people who are still on the older major versions).
A compatible compiler (see instructions for different tools below).
Since the new JSX transform doesn’t require React to be in scope, we’ve also prepared an automated script that will remove the unnecessary imports from your codebase.
Create React App
Create React App 4.0.0+ uses the new transform for compatible React versions.
Next.js
Next.js v9.5.3+ uses the new transform for compatible React versions.
Gatsby
Gatsby v2.24.5+ uses the new transform for compatible React versions.
Note
If you get this Gatsby error after upgrading to React 17 RC, run npm update to fix it.
Manual Babel Setup
Support for the new JSX transform is available in Babel v7.9.0 and above.
First, you’ll need to update to the latest Babel and plugin transform.
If you are using @babel/plugin-transform-react-jsx:
# for npm usersnpm update @babel/core @babel/plugin-transform-react-jsx
# for yarn usersyarn upgrade @babel/core @babel/plugin-transform-react-jsx
If you are using @babel/preset-react:
# for npm usersnpm update @babel/core @babel/preset-react
# for yarn usersyarn upgrade @babel/core @babel/preset-react
Currently, the old transform {"runtime": "classic"} is the default option. To enable the new transform, you can pass {"runtime": "automatic"} as an option to @babel/plugin-transform-react-jsx or @babel/preset-react:
// If you are using @babel/preset-react{"presets":[["@babel/preset-react",{"runtime":"automatic"}]]}
// If you're using @babel/plugin-transform-react-jsx{"plugins":[["@babel/plugin-transform-react-jsx",{"runtime":"automatic"}]]}
If you use JSX with a library other than React, you can use the importSource option to import from that library instead — as long as it provides the necessary entry points. Alternatively, you can keep using the classic transform which will continue to be supported.
If you’re a library author and you are implementing the /jsx-runtime entry point for your library, keep in mind that there is a case in which even the new transform has to fall back to createElement for backwards compatibility. In that case, it will auto-import createElement directly from the root entry point specified by importSource.
ESLint
If you are using eslint-plugin-react, the react/jsx-uses-react and react/react-in-jsx-scope rules are no longer necessary and can be turned off or removed.
TypeScript supports the new JSX transform in v4.1 and up.
Flow
Flow supports the new JSX transform in v0.126.0 and up, by adding react.runtime=automatic to your Flow configuration options.
Removing Unused React Imports
Because the new JSX transform will automatically import the necessary react/jsx-runtime functions, React will no longer need to be in scope when you use JSX. This might lead to unused React imports in your code. It doesn’t hurt to keep them, but if you’d like to remove them, we recommend running a “codemod” script to remove them automatically:
cd your_project
npx react-codemod update-react-imports
Note
If you’re getting errors when running the codemod, try specifying a different JavaScript dialect when npx react-codemod update-react-imports asks you to choose one. In particular, at this moment the “JavaScript with Flow” setting supports newer syntax than the “JavaScript” setting even if you don’t use Flow. File an issue if you run into problems.
Keep in mind that the codemod output will not always match your project’s coding style, so you might want to run Prettier after the codemod finishes for consistent formatting.
Running this codemod will:
Remove all unused React imports as a result of upgrading to the new JSX transform.
Change all default React imports (i.e. import React from "react") to destructured named imports (ex. import { useState } from "react") which is the preferred style going into the future. This codemod will not affect the existing namespace imports (i.e. import * as React from "react") which is also a valid style. The default imports will keep working in React 17, but in the longer term we encourage moving away from them.
In addition to cleaning up unused imports, this will also help you prepare for a future major version of React (not React 17) which will support ES Modules and not have a default export.
Thanks
We’d like to thank Babel, TypeScript, Create React App, Next.js, Gatsby, ESLint, and Flow maintainers for their help implementing and integrating the new JSX transform. We also want to thank the React community for their feedback and discussion on the related technical RFC.
This blog site has been archived. Go to react.dev/blog to see the recent posts. Today, we are publishing the first Release Candidate for React 17. It has been two and a half years since the previous major release of React, which is a long time even by our standards! In this...
This blog site has been archived. Go to react.dev/blog to see the recent posts.
Today, we are publishing the first Release Candidate for React 17. It has been two and a half years since the previous major release of React, which is a long time even by our standards! In this blog post, we will describe the role of this major release, what changes you can expect in it, and how you can try this release.
No New Features
The React 17 release is unusual because it doesn’t add any new developer-facing features. Instead, this release is primarily focused on making it easier to upgrade React itself.
We’re actively working on the new React features, but they’re not a part of this release. The React 17 release is a key part of our strategy to roll them out without leaving anyone behind.
In particular, React 17 is a “stepping stone” release that makes it safer to embed a tree managed by one version of React inside a tree managed by a different version of React.
Gradual Upgrades
For the past seven years, React upgrades have been “all-or-nothing”. Either you stay on an old version, or you upgrade your whole app to a new version. There was no in-between.
This has worked out so far, but we are running into the limits of the “all-or-nothing” upgrade strategy. Some API changes, for example, deprecating the legacy context API, are impossible to do in an automated way. Even though most apps written today don’t ever use them, we still support them in React. We have to choose between supporting them in React indefinitely or leaving some apps behind on an old version of React. Both of these options aren’t great.
So we wanted to provide another option.
React 17 enables gradual React upgrades. When you upgrade from React 15 to 16 (or, soon, from React 16 to 17), you would usually upgrade your whole app at once. This works well for many apps. But it can get increasingly challenging if the codebase was written more than a few years ago and isn’t actively maintained. And while it’s possible to use two versions of React on the page, until React 17 this has been fragile and caused problems with events.
We’re fixing many of those problems with React 17. This means that when React 18 and the next future versions come out, you will now have more options. The first option will be to upgrade your whole app at once, like you might have done before. But you will also have an option to upgrade your app piece by piece. For example, you might decide to migrate most of your app to React 18, but keep some lazy-loaded dialog or a subroute on React 17.
This doesn’t mean you have to do gradual upgrades. For most apps, upgrading all at once is still the best solution. Loading two versions of React — even if one of them is loaded lazily on demand — is still not ideal. However, for larger apps that aren’t actively maintained, this option may make sense to consider, and React 17 enables those apps to not get left behind.
To enable gradual updates, we’ve needed to make some changes to the React event system. React 17 is a major release because these changes are potentially breaking. In practice, we’ve only had to change fewer than twenty components out of 100,000+ so we expect that most apps can upgrade to React 17 without too much trouble. Tell us if you run into problems.
Demo of Gradual Upgrades
We’ve prepared an example repository demonstrating how to lazy-load an older version of React if necessary. This demo uses Create React App, but it should be possible to follow a similar approach with any other tool. We welcome demos using other tooling as pull requests.
Note
We’ve postponed other changes until after React 17. The goal of this release is to enable gradual upgrades. If upgrading to React 17 were too difficult, it would defeat its purpose.
Changes to Event Delegation
Technically, it has always been possible to nest apps developed with different versions of React. However, it was rather fragile because of how the React event system worked.
In React components, you usually write event handlers inline:
<buttononClick={handleClick}>
The vanilla DOM equivalent to this code is something like:
myButton.addEventListener('click', handleClick);
However, for most events, React doesn’t actually attach them to the DOM nodes on which you declare them. Instead, React attaches one handler per event type directly at the document node. This is called event delegation. In addition to its performance benefits on large application trees, it also makes it easier to add new features like replaying events.
React has been doing event delegation automatically since its first release. When a DOM event fires on the document, React figures out which component to call, and then the React event “bubbles” upwards through your components. But behind the scenes, the native event has already bubbled up to the document level, where React installs its event handlers.
However, this is a problem for gradual upgrades.
If you have multiple React versions on the page, they all register event handlers at the top. This breaks e.stopPropagation(): if a nested tree has stopped propagation of an event, the outer tree would still receive it. This made it difficult to nest different versions of React. This concern is not hypothetical — for example, the Atom editor ran into this four years ago.
This is why we’re changing how React attaches events to the DOM under the hood.
In React 17, React will no longer attach event handlers at the document level. Instead, it will attach them to the root DOM container into which your React tree is rendered:
In React 16 and earlier, React would do document.addEventListener() for most events. React 17 will call rootNode.addEventListener() under the hood instead.
Thanks to this change, it is now safer to embed a React tree managed by one version inside a tree managed by a different React version. Note that for this to work, both of the versions would need to be 17 or higher, which is why upgrading to React 17 is important. In a way, React 17 is a “stepping stone” release that makes next gradual upgrades feasible.
This change also makes it easier to embed React into apps built with other technologies. For example, if the outer “shell” of your app is written in jQuery, but the newer code inside of it is written with React, e.stopPropagation() inside the React code would now prevent it from reaching the jQuery code — as you would expect. This also works in the other direction. If you no longer like React and want to rewrite your app — for example, in jQuery — you can start converting the outer shell from React to jQuery without breaking the event propagation.
You might be wondering whether this breaks Portals outside of the root container. The answer is that React also listens to events on portal containers, so this is not an issue.
Fixing Potential Issues
As with any breaking change, it is likely some code would need to be adjusted. At Facebook, we had to adjust about 10 modules in total (out of many thousands) to work with this change.
For example, if you add manual DOM listeners with document.addEventListener(...), you might expect them to catch all React events. In React 16 and earlier, even if you call e.stopPropagation() in a React event handler, your custom document listeners would still receive them because the native event is already at the document level. With React 17, the propagation would stop (as requested!), so your document handlers would not fire:
document.addEventListener('click',function(){// This custom handler will no longer receive clicks// from React components that called e.stopPropagation()});
You can fix code like this by converting your listener to use the capture phase. To do this, you can pass { capture: true } as the third argument to document.addEventListener:
document.addEventListener('click',function(){// Now this event handler uses the capture phase,// so it receives *all* click events below!},{capture:true});
Note how this strategy is more resilient overall — for example, it will probably fix existing bugs in your code that happen when e.stopPropagation() is called outside of a React event handler. In other words, event propagation in React 17 works closer to the regular DOM.
Other Breaking Changes
We’ve kept the breaking changes in React 17 to the minimum. For example, it doesn’t remove any of the methods that have been deprecated in the previous releases. However, it does include a few other breaking changes that have been relatively safe in our experience. In total, we’ve had to adjust fewer than 20 out of 100,000+ our components because of them.
Aligning with Browsers
We’ve made a couple of smaller changes related to the event system:
The onScroll event no longer bubbles to prevent common confusion.
React onFocus and onBlur events have switched to using the native focusin and focusout events under the hood, which more closely match React’s existing behavior and sometimes provide extra information.
Capture phase events (e.g. onClickCapture) now use real browser capture phase listeners.
These changes align React closer with the browser behavior and improve interoperability.
Note
Although React 17 switched from focus to focusinunder the hood for the onFocus event, note that this has not affected the bubbling behavior. In React, onFocus event has always bubbled, and it continues to do so in React 17 because generally it is a more useful default. See this sandbox for the different checks you can add for different particular use cases.
No Event Pooling
React 17 removes the “event pooling” optimization from React. It doesn’t improve performance in modern browsers and confuses even experienced React users:
functionhandleChange(e){setData(data=>({...data,// This crashes in React 16 and earlier:text: e.target.value
}));}
This is because React reused the event objects between different events for performance in old browsers, and set all event fields to null in between them. With React 16 and earlier, you have to call e.persist() to properly use the event, or read the property you need earlier.
In React 17, this code works as you would expect. The old event pooling optimization has been fully removed, so you can read the event fields whenever you need them.
This is a behavior change, which is why we’re marking it as breaking, but in practice we haven’t seen it break anything at Facebook. (Maybe it even fixed a few bugs!) Note that e.persist() is still available on the React event object, but now it doesn’t do anything.
Effect Cleanup Timing
We are making the timing of the useEffect cleanup function more consistent.
useEffect(()=>{// This is the effect itself.return()=>{// This is its cleanup.};});
Most effects don’t need to delay screen updates, so React runs them asynchronously soon after the update has been reflected on the screen. (For the rare cases where you need an effect to block paint, e.g. to measure and position a tooltip, prefer useLayoutEffect.)
However, when a component is unmounting, effect cleanup functions used to run synchronously (similar to componentWillUnmount being synchronous in classes). We’ve found that this is not ideal for larger apps because it slows down large screen transitions (e.g. switching tabs).
In React 17, the effect cleanup function always runs asynchronously — for example, if the component is unmounting, the cleanup runs after the screen has been updated.
This mirrors how the effects themselves run more closely. In the rare cases where you might want to rely on the synchronous execution, you can switch to useLayoutEffect instead.
Note
You might be wondering whether this means that you’ll now be unable to fix warnings about setState on unmounted components. Don’t worry — React specifically checks for this case, and does not fire setState warnings in the short gap between unmounting and the cleanup. So code cancelling requests or intervals can almost always stay the same.
Additionally, React 17 will always execute all effect cleanup functions (for all components) before it runs any new effects. React 16 only guaranteed this ordering for effects within a component.
Potential Issues
We’ve only seen a couple of components break with this change, although reusable libraries may need to test it more thoroughly. One example of problematic code may look like this:
The problem is that someRef.current is mutable, so by the time the cleanup function runs, it may have been set to null. The solution is to capture any mutable values inside the effect:
In React 16 and earlier, returning undefined has always been an error:
functionButton(){return;// Error: Nothing was returned from render}
This is in part because it’s easy to return undefined unintentionally:
functionButton(){// We forgot to write return, so this component returns undefined.// React surfaces this as an error instead of ignoring it.<button/>;}
Previously, React only did this for class and function components, but did not check the return values of forwardRef and memo components. This was due to a coding mistake.
In React 17, the behavior for forwardRef and memo components is consistent with regular function and class components. Returning undefined from them is an error.
let Button =forwardRef(()=>{// We forgot to write return, so this component returns undefined.// React 17 surfaces this as an error instead of ignoring it.<button/>;});let Button =memo(()=>{// We forgot to write return, so this component returns undefined.// React 17 surfaces this as an error instead of ignoring it.<button/>;});
For the cases where you want to render nothing intentionally, return null instead.
Native Component Stacks
When you throw an error in the browser, the browser gives you a stack trace with JavaScript function names and their locations. However, JavaScript stacks are often not enough to diagnose a problem because the React tree hierarchy can be just as important. You want to know not just that a Button threw an error, but where in the React tree that Button is.
To solve this, React 16 started printing “component stacks” when you have an error. Still, they used to be inferior to the native JavaScript stacks. In particular, they were not clickable in the console because React didn’t know where the function was declared in the source code. Additionally, they were mostly useless in production. Unlike regular minified JavaScript stacks which can be automatically restored to the original function names with a sourcemap, with React component stacks you had to choose between production stacks and bundle size.
In React 17, the component stacks are generated using a different mechanism that stitches them together from the regular native JavaScript stacks. This lets you get the fully symbolicated React component stack traces in a production environment.
The way React implements this is somewhat unorthodox. Currently, the browsers don’t provide a way to get a function’s stack frame (source file and location). So when React catches an error, it will now reconstruct its component stack by throwing (and catching) a temporary error from inside each of the components above, when it is possible. This adds a small performance penalty for crashes, but it only happens once per component type.
If you’re curious, you can read more details in this pull request, but for the most part this exact mechanism shouldn’t affect your code. From your perspective, the new feature is that component stacks are now clickable (because they rely on the native browser stack frames), and that you can decode them in production like you would with regular JavaScript errors.
The part that constitutes a breaking change is that for this to work, React re-executes parts of some of the React functions and React class constructors above in the stack after an error is captured. Since render functions and class constructors shouldn’t have side effects (which is also important for server rendering), this should not pose any practical problems.
Removing Private Exports
Finally, the last notable breaking change is that we’ve removed some React internals that were previously exposed to other projects. In particular, React Native for Web used to depend on some internals of the event system, but that dependency was fragile and used to break.
In React 17, these private exports have been removed. As far as we’re aware, React Native for Web was the only project using them, and they have already completed a migration to a different approach that doesn’t depend on those private exports.
This means that the older versions of React Native for Web won’t be compatible with React 17, but the newer versions will work with it. In practice, this doesn’t change much because React Native for Web had to release new versions to adapt to internal React changes anyway.
Additionally, we’ve removed the ReactTestUtils.SimulateNative helper methods. They have never been documented, didn’t do quite what their names implied, and didn’t work with the changes we’ve made to the event system. If you want a convenient way to fire native browser events in tests, check out the React Testing Library instead.
Installation
We encourage you to try React 17.0 Release Candidate soon and raise any issues for the problems you might encounter in the migration. Keep in mind that a release candidate is more likely to contain bugs than a stable release, so don’t deploy it to production yet.
This blog site has been archived. Go to react.dev/blog to see the recent posts. Today we are releasing React 16.13.0. It contains bugfixes and new deprecation warnings to help prepare for a future major release. New Warnings Warnings for some updates during render A React...
This blog site has been archived. Go to react.dev/blog to see the recent posts.
Today we are releasing React 16.13.0. It contains bugfixes and new deprecation warnings to help prepare for a future major release.
New Warnings
Warnings for some updates during render
A React component should not cause side effects in other components during rendering.
It is supported to call setState during render, but only for the same component. If you call setState during a render on a different component, you will now see a warning:
Warning: Cannot update a component from inside the function body of a different component.
This warning will help you find application bugs caused by unintentional state changes. In the rare case that you intentionally want to change the state of another component as a result of rendering, you can wrap the setState call into useEffect.
Warnings for conflicting style rules
When dynamically applying a style that contains longhand and shorthand versions of CSS properties, particular combinations of updates can cause inconsistent styling. For example:
You might expect this <div> to always have a red background, no matter the value of toggle. However, on alternating the value of toggle between true and false, the background color start as red, then alternates between transparent and blue, as you can see in this demo.
React now detects conflicting style rules and logs a warning. To fix the issue, don’t mix shorthand and longhand versions of the same CSS property in the style prop.
(Don’t confuse String Refs with refs in general, which remain fully supported.)
In the future, we will provide an automated script (a “codemod”) to migrate away from String Refs. However, some rare cases can’t be migrated automatically. This release adds a new warning only for those cases in advance of the deprecation.
For example, it will fire if you use String Refs together with the Render Prop pattern:
To see this warning, you need to have the babel-plugin-transform-react-jsx-self installed in your Babel plugins. It must only be enabled in development mode.
If you use Create React App or have the “react” preset with Babel 7+, you already have this plugin installed by default.
Deprecating React.createFactory
React.createFactory is a legacy helper for creating React elements. This release adds a deprecation warning to the method. It will be removed in a future major version.
Replace usages of React.createFactory with regular JSX. Alternately, you can copy and paste this one-line helper or publish it as a library:
Deprecating ReactDOM.unstable_createPortal in favor of ReactDOM.createPortal
When React 16 was released, createPortal became an officially supported API.
However, we kept unstable_createPortal as a supported alias to keep the few libraries that adopted it working. We are now deprecating the unstable alias. Use createPortal directly instead of unstable_createPortal. It has exactly the same signature.
Other Improvements
Component stacks in hydration warnings
React adds component stacks to its development warnings, enabling developers to isolate bugs and debug their programs. This release adds component stacks to a number of development warnings that didn’t previously have them. As an example, consider this hydration warning from the previous versions:
While it’s pointing out an error with the code, it’s not clear where the error exists, and what to do next. This release adds a component stack to this warning, which makes it look like this:
This makes it clear where the problem is, and lets you locate and fix the bug faster.
Notable bugfixes
This release contains a few other notable improvements:
In Strict Development Mode, React calls lifecycle methods twice to flush out any possible unwanted side effects. This release adds that behaviour to shouldComponentUpdate. This shouldn’t affect most code, unless you have side effects in shouldComponentUpdate. To fix this, move the code with side effects into componentDidUpdate.
In Strict Development Mode, the warnings for usage of the legacy context API didn’t include the stack for the component that triggered the warning. This release adds the missing stack to the warning.
onMouseEnter now doesn’t trigger on disabled <button> elements.
ReactDOM was missing a version export since we published v16. This release adds it back. We don’t recommend using it in your application logic, but it’s useful when debugging issues with mismatching / multiple versions of ReactDOM on the same page.
We’re thankful to all the contributors who helped surface and fix these and other issues. You can find the full changelog below.