React.js Blog

Follow this blog

Introducing Zero-Bundle-Size React Server Components

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. Read the RFC (with FAQ at the end) for a deeper technical breakdown and to provide feedback. We are excited to hear from you on the RFC or in replies to the @reactjs Twitter handle. Happy holidays, stay safe, and see you next year!

React v17.0

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: const rootNode = document.getElementById('root'); ReactDOM.render(<App />, rootNode); 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 confirmed that numerous problems reported over the years on our issue tracker related to integrating React with non-React code have been fixed by the new behavior. If you run into issues with this change, here’s a common way to resolve them. Other Breaking Changes The React 17 RC blog post describes the rest of the breaking changes in React 17. 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. Installation To install React 17 with npm, run: npm install react@17.0.0 react-dom@17.0.0 To install React 17 with Yarn, run: yarn add react@17.0.0 react-dom@17.0.0 We also provide UMD builds of React via a CDN: <script crossorigin src="https://unpkg.com/react@17.0.0/umd/react.production.min.js"></script> <script crossorigin src="https://unpkg.com/react-dom@17.0.0/umd/react-dom.production.min.js"></script> Refer to the documentation for detailed installation instructions. Changelog React Add react/jsx-runtime and react/jsx-dev-runtime for the new JSX transform. (@lunaruan in #18299) Build component stacks from native error frames. (@sebmarkbage in #18561) Allow to specify displayName on context for improved stacks. (@eps1lon in #18224) Prevent 'use strict' from leaking in the UMD bundles. (@koba04 in #19614) Stop using fb.me for redirects. (@cylim in #19598) React DOM Delegate events to roots instead of document. (@trueadm in #18195 and others) Clean up all effects before running any next effects. (@bvaughn in #17947) Run useEffect cleanup functions asynchronously. (@bvaughn in #17925) Use browser focusin and focusout for onFocus and onBlur. (@trueadm in #19186) Make all Capture events use the browser capture phase. (@trueadm in #19221) Don’t emulate bubbling of the onScroll event. (@gaearon in #19464) Throw if forwardRef or memo component returns undefined. (@gaearon in #19550) Remove event pooling. (@trueadm in #18969) Stop exposing internals that won’t be needed by React Native Web. (@necolas in #18483) Attach all known event listeners when the root mounts. (@gaearon in #19659) Disable console in the second render pass of DEV mode double render. (@sebmarkbage in #18547) Deprecate the undocumented and misleading ReactTestUtils.SimulateNative API. (@gaearon in #13407) Rename private field names used in the internals. (@gaearon in #18377) Don’t call User Timing API in development. (@gaearon in #18417) Disable console during the repeated render in Strict Mode. (@sebmarkbage in #18547) In Strict Mode, double-render components without Hooks too. (@eps1lon in #18430) Allow calling ReactDOM.flushSync during lifecycle methods (but warn). (@sebmarkbage in #18759) Add the code property to the keyboard event objects. (@bl00mber in #18287) Add the disableRemotePlayback property for video elements. (@tombrowndev in #18619) Add the enterKeyHint property for input elements. (@eps1lon in #18634) Warn when no value is provided to <Context.Provider>. (@charlie1404 in #19054) Warn when memo or forwardRef components return undefined. (@bvaughn in #19550) Improve the error message for invalid updates. (@JoviDeCroock in #18316) Exclude forwardRef and memo from stack frames. (@sebmarkbage in #18559) Improve the error message when switching between controlled and uncontrolled inputs. (@vcarl in #17070) Keep onTouchStart, onTouchMove, and onWheel passive. (@gaearon in #19654) Fix setState hanging in development inside a closed iframe. (@gaearon in #19220) Fix rendering bailout for lazy components with defaultProps. (@jddxf in #18539) Fix a false positive warning when dangerouslySetInnerHTML is undefined. (@eps1lon in #18676) Fix Test Utils with non-standard require implementation. (@just-boris in #18632) Fix onBeforeInput reporting an incorrect event.type. (@eps1lon in #19561) Fix event.relatedTarget reported as undefined in Firefox. (@claytercek in #19607) Fix “unspecified error” in IE11. (@hemakshis in #19664) Fix rendering into a shadow root. (@Jack-Works in #15894) Fix movementX/Y polyfill with capture events. (@gaearon in #19672) Use delegation for onSubmit and onReset events. (@gaearon in #19333) Improve memory usage. (@trueadm in #18970) React DOM Server Make useCallback behavior consistent with useMemo for the server renderer. (@alexmckenley in #18783) Fix state leaking when a function component throws. (@pmaccart in #19212) React Test Renderer Improve findByType error message. (@henryqdineen in #17439) Concurrent Mode (Experimental) Revamp the priority batching heuristics. (@acdlite in #18796) Add the unstable_ prefix before the experimental APIs. (@acdlite in #18825) Remove unstable_discreteUpdates and unstable_flushDiscreteUpdates. (@trueadm in #18825) Remove the timeoutMs argument. (@acdlite in #19703) Disable <div hidden /> prerendering in favor of a different future API. (@acdlite in #18917) Add unstable_expectedLoadTime to Suspense for CPU-bound trees. (@acdlite in #19936) Add an experimental unstable_useOpaqueIdentifier Hook. (@lunaruan in #17322) Add an experimental unstable_startTransition API. (@rickhanlonii in #19696) Using act in the test renderer no longer flushes Suspense fallbacks. (@acdlite in #18596) Use global render timeout for CPU Suspense. (@sebmarkbage in #19643) Clear the existing root content before mounting. (@bvaughn in #18730) Fix a bug with error boundaries. (@acdlite in #18265) Fix a bug causing dropped updates in a suspended tree. (@acdlite in #18384 and #18457) Fix a bug causing dropped render phase updates. (@acdlite in #18537) Fix a bug in SuspenseList. (@sebmarkbage in #18412) Fix a bug causing Suspense fallback to show too early. (@acdlite in #18411) Fix a bug with class components inside SuspenseList. (@sebmarkbage in #18448) Fix a bug with inputs that may cause updates to be dropped. (@jddxf in #18515 and @acdlite in #18535) Fix a bug causing Suspense fallback to get stuck. (@acdlite in #18663) Don’t cut off the tail of a SuspenseList if hydrating. (@sebmarkbage in #18854) Fix a bug in useMutableSource that may happen when getSnapshot changes. (@bvaughn in #18297) Fix a tearing bug in useMutableSource. (@bvaughn in #18912) Warn if calling setState outside of render but before commit. (@sebmarkbage in #18838)

Introducing the New JSX Transform

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: import React from 'react'; function App() { return <h1>Hello World</h1>; } Under the hood, the old JSX transform turns it into regular JavaScript: import React from 'react'; function App() { return React.createElement('h1', null, 'Hello world'); } Note 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. There are some performance improvements and simplifications that React.createElement does not allow. 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: function App() { 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'; function App() { 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 users npm update @babel/core @babel/plugin-transform-react-jsx # for yarn users yarn upgrade @babel/core @babel/plugin-transform-react-jsx If you are using @babel/preset-react: # for npm users npm update @babel/core @babel/preset-react # for yarn users yarn 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" }] ] } Starting from Babel 8, "automatic" will be the default runtime for both plugins. For more information, check out the Babel documentation for @babel/plugin-transform-react-jsx and @babel/preset-react. Note 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. { // ... "rules": { // ... "react/jsx-uses-react": "off", "react/react-in-jsx-scope": "off" } } TypeScript 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. For example, import React from 'react'; function App() { return <h1>Hello World</h1>; } will be replaced with function App() { return <h1>Hello World</h1>; } If you use some other import from React — for example, a Hook — then the codemod will convert it to a named import. For example, import React from 'react'; function App() { const [text, setText] = React.useState('Hello World'); return <h1>{text}</h1>; } will be replaced with import { useState } from 'react'; function App() { const [text, setText] = useState('Hello World'); return <h1>{text}</h1>; } 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.

React v17.0 Release Candidate: No New Features

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: <button onClick={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: const rootNode = document.getElementById('root'); ReactDOM.render(<App />, rootNode); 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. We’ve confirmed that numerous problems reported over the years on our issue tracker related to integrating React with non-React code have been fixed by the new behavior. Note 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 focusin under 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: function handleChange(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: useEffect(() => { someRef.current.someSetupMethod(); return () => { someRef.current.someCleanupMethod(); }; }); 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: useEffect(() => { const instance = someRef.current; instance.someSetupMethod(); return () => { instance.someCleanupMethod(); }; }); We don’t expect this to be a common problem because our eslint-plugin-react-hooks/exhaustive-deps lint rule (make sure you use it!) has always warned about this. Consistent Errors for Returning Undefined In React 16 and earlier, returning undefined has always been an error: function Button() { return; // Error: Nothing was returned from render } This is in part because it’s easy to return undefined unintentionally: function Button() { // 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. To install React 17 RC with npm, run: npm install react@17.0.0-rc.3 react-dom@17.0.0-rc.3 To install React 17 RC with Yarn, run: yarn add react@17.0.0-rc.3 react-dom@17.0.0-rc.3 We also provide UMD builds of React via a CDN: <script crossorigin src="https://unpkg.com/react@17.0.0-rc.3/umd/react.production.min.js"></script> <script crossorigin src="https://unpkg.com/react-dom@17.0.0-rc.3/umd/react-dom.production.min.js"></script> Refer to the documentation for detailed installation instructions. Changelog React Add react/jsx-runtime and react/jsx-dev-runtime for the new JSX transform. (@lunaruan in #18299) Build component stacks from native error frames. (@sebmarkbage in #18561) Allow to specify displayName on context for improved stacks. (@eps1lon in #18224) Prevent 'use strict' from leaking in the UMD bundles. (@koba04 in #19614) Stop using fb.me for redirects. (@cylim in #19598) React DOM Delegate events to roots instead of document. (@trueadm in #18195 and others) Clean up all effects before running any next effects. (@bvaughn in #17947) Run useEffect cleanup functions asynchronously. (@bvaughn in #17925) Use browser focusin and focusout for onFocus and onBlur. (@trueadm in #19186) Make all Capture events use the browser capture phase. (@trueadm in #19221) Don’t emulate bubbling of the onScroll event. (@gaearon in #19464) Throw if forwardRef or memo component returns undefined. (@gaearon in #19550) Remove event pooling. (@trueadm in #18969) Stop exposing internals that won’t be needed by React Native Web. (@necolas in #18483) Attach all known event listeners when the root mounts. (@gaearon in #19659) Disable console in the second render pass of DEV mode double render. (@sebmarkbage in #18547) Deprecate the undocumented and misleading ReactTestUtils.SimulateNative API. (@gaearon in #13407) Rename private field names used in the internals. (@gaearon in #18377) Don’t call User Timing API in development. (@gaearon in #18417) Disable console during the repeated render in Strict Mode. (@sebmarkbage in #18547) In Strict Mode, double-render components without Hooks too. (@eps1lon in #18430) Allow calling ReactDOM.flushSync during lifecycle methods (but warn). (@sebmarkbage in #18759) Add the code property to the keyboard event objects. (@bl00mber in #18287) Add the disableRemotePlayback property for video elements. (@tombrowndev in #18619) Add the enterKeyHint property for input elements. (@eps1lon in #18634) Warn when no value is provided to <Context.Provider>. (@charlie1404 in #19054) Warn when memo or forwardRef components return undefined. (@bvaughn in #19550) Improve the error message for invalid updates. (@JoviDeCroock in #18316) Exclude forwardRef and memo from stack frames. (@sebmarkbage in #18559) Improve the error message when switching between controlled and uncontrolled inputs. (@vcarl in #17070) Keep onTouchStart, onTouchMove, and onWheel passive. (@gaearon in #19654) Fix setState hanging in development inside a closed iframe. (@gaearon in #19220) Fix rendering bailout for lazy components with defaultProps. (@jddxf in #18539) Fix a false positive warning when dangerouslySetInnerHTML is undefined. (@eps1lon in #18676) Fix Test Utils with non-standard require implementation. (@just-boris in #18632) Fix onBeforeInput reporting an incorrect event.type. (@eps1lon in #19561) Fix event.relatedTarget reported as undefined in Firefox. (@claytercek in #19607) Fix “unspecified error” in IE11. (@hemakshis in #19664) Fix rendering into a shadow root. (@Jack-Works in #15894) Fix movementX/Y polyfill with capture events. (@gaearon in #19672) Use delegation for onSubmit and onReset events. (@gaearon in #19333) Improve memory usage. (@trueadm in #18970) React DOM Server Make useCallback behavior consistent with useMemo for the server renderer. (@alexmckenley in #18783) Fix state leaking when a function component throws. (@pmaccart in #19212) React Test Renderer Improve findByType error message. (@henryqdineen in #17439) Concurrent Mode (Experimental) Revamp the priority batching heuristics. (@acdlite in #18796) Add the unstable_ prefix before the experimental APIs. (@acdlite in #18825) Remove unstable_discreteUpdates and unstable_flushDiscreteUpdates. (@trueadm in #18825) Remove the timeoutMs argument. (@acdlite in #19703) Disable <div hidden /> prerendering in favor of a different future API. (@acdlite in #18917) Add unstable_expectedLoadTime to Suspense for CPU-bound trees. (@acdlite in #19936) Add an experimental unstable_useOpaqueIdentifier Hook. (@lunaruan in #17322) Add an experimental unstable_startTransition API. (@rickhanlonii in #19696) Using act in the test renderer no longer flushes Suspense fallbacks. (@acdlite in #18596) Use global render timeout for CPU Suspense. (@sebmarkbage in #19643) Clear the existing root content before mounting. (@bvaughn in #18730) Fix a bug with error boundaries. (@acdlite in #18265) Fix a bug causing dropped updates in a suspended tree. (@acdlite in #18384 and #18457) Fix a bug causing dropped render phase updates. (@acdlite in #18537) Fix a bug in SuspenseList. (@sebmarkbage in #18412) Fix a bug causing Suspense fallback to show too early. (@acdlite in #18411) Fix a bug with class components inside SuspenseList. (@sebmarkbage in #18448) Fix a bug with inputs that may cause updates to be dropped. (@jddxf in #18515 and @acdlite in #18535) Fix a bug causing Suspense fallback to get stuck. (@acdlite in #18663) Don’t cut off the tail of a SuspenseList if hydrating. (@sebmarkbage in #18854) Fix a bug in useMutableSource that may happen when getSnapshot changes. (@bvaughn in #18297) Fix a tearing bug in useMutableSource. (@bvaughn in #18912) Warn if calling setState outside of render but before commit. (@sebmarkbage in #18838)

React v16.13.0

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: <div style={toggle ? { background: 'blue', backgroundColor: 'red' } : { backgroundColor: 'red' } }> ... </div> 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. Warnings for some deprecated string refs String Refs is an old legacy API which is discouraged and is going to be deprecated in the future: <Button ref="myRef" /> (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: class ClassWithRenderProp extends React.Component { componentDidMount() { doSomething(this.refs.myRef); } render() { return this.props.children(); } } class ClassParent extends React.Component { render() { return ( <ClassWithRenderProp> {() => <Button ref="myRef" />} </ClassWithRenderProp> ); } } Code like this often indicates bugs. (You might expect the ref to be available on ClassParent, but instead it gets placed on ClassWithRenderProp). You most likely don’t have code like this. If you do and it is intentional, convert it to React.createRef() instead: class ClassWithRenderProp extends React.Component { myRef = React.createRef(); componentDidMount() { doSomething(this.myRef.current); } render() { return this.props.children(this.myRef); } } class ClassParent extends React.Component { render() { return ( <ClassWithRenderProp> {myRef => <Button ref={myRef} />} </ClassWithRenderProp> ); } } Note 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: let createFactory = type => React.createElement.bind(null, type); It does exactly the same thing. 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. Installation React React v16.13.0 is available on the npm registry. To install React 16 with Yarn, run: yarn add react@^16.13.0 react-dom@^16.13.0 To install React 16 with npm, run: npm install --save react@^16.13.0 react-dom@^16.13.0 We also provide UMD builds of React via a CDN: <script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script> Refer to the documentation for detailed installation instructions. Changelog React Warn when a string ref is used in a manner that’s not amenable to a future codemod (@lunaruan in #17864) Deprecate React.createFactory() (@trueadm in #17878) React DOM Warn when changes in style may cause an unexpected collision (@sophiebits in #14181, #18002) Warn when a function component is updated during another component’s render phase (@acdlite in #17099) Deprecate unstable_createPortal (@trueadm in #17880) Fix onMouseEnter being fired on disabled buttons (@AlfredoGJ in #17675) Call shouldComponentUpdate twice when developing in StrictMode (@bvaughn in #17942) Add version property to ReactDOM (@ealush in #15780) Don’t call toString() of dangerouslySetInnerHTML (@sebmarkbage in #17773) Show component stacks in more warnings (@gaearon in #17922, #17586) Concurrent Mode (Experimental) Warn for problematic usages of ReactDOM.createRoot() (@trueadm in #17937) Remove ReactDOM.createRoot() callback params and added warnings on usage (@bvaughn in #17916) Don’t group Idle/Offscreen work with other work (@sebmarkbage in #17456) Adjust SuspenseList CPU bound heuristic (@sebmarkbage in #17455) Add missing event plugin priorities (@trueadm in #17914) Fix isPending only being true when transitioning from inside an input event (@acdlite in #17382) Fix React.memo components dropping updates when interrupted by a higher priority update (@acdlite in #18091) Don’t warn when suspending at the wrong priority (@gaearon in #17971) Fix a bug with rebasing updates (@acdlite and @sebmarkbage in #17560, #17510, #17483, #17480)

Building Great User Experiences with Concurrent Mode and Suspense

At React Conf 2019 we announced an experimental release of React that supports Concurrent Mode and Suspense. In this post we’ll introduce best practices for using them that we’ve identified through the process of building the new facebook.com. This post will be most relevant to people working on data fetching libraries for React. It shows how to best integrate them with Concurrent Mode and Suspense. The patterns introduced here are based on Relay — our library for building data-driven UIs with GraphQL. However, the ideas in this post apply to other GraphQL clients as well as libraries using REST or other approaches. This post is aimed at library authors. If you’re primarily an application developer, you might still find some interesting ideas here, but don’t feel like you have to read it in its entirety. Talk Videos If you prefer to watch videos, some of the ideas from this blog post have been referenced in several React Conf 2019 presentations: Data Fetching with Suspense in Relay by Joe Savona Building the New Facebook with React and Relay by Ashley Watkins React Conf Keynote by Yuzhi Zheng This post presents a deeper dive on implementing a data fetching library with Suspense. Putting User Experience First The React team and community has long placed a deserved emphasis on developer experience: ensuring that React has good error messages, focusing on components as a way to reason locally about app behavior, crafting APIs that are predictable and encourage correct usage by design, etc. But we haven’t provided enough guidance on the best ways to achieve a great user experience in large apps. For example, the React team has focused on framework performance and providing tools for developers to debug and tune application performance (e.g. React.memo). But we haven’t been as opinionated about the high-level patterns that make the difference between fast, fluid apps and slow, janky ones. We always want to ensure that React remains approachable to new users and supports a variety of use-cases — not every app has to be “blazing” fast. But as a community we can and should aim high. We should make it as easy as possible to build apps that start fast and stay fast, even as they grow in complexity, for users on varying devices and networks around the world. Concurrent Mode and Suspense are experimental features that can help developers achieve this goal. We first introduced them at JSConf Iceland in 2018, intentionally sharing details very early to give the community time to digest the new concepts and to set the stage for subsequent changes. Since then we’ve completed related work, such as the new Context API and the introduction of Hooks, which are designed in part to help developers naturally write code that is more compatible with Concurrent Mode. But we didn’t want to implement these features and release them without validating that they work. So over the past year, the React, Relay, web infrastructure, and product teams at Facebook have all collaborated closely to build a new version of facebook.com that deeply integrates Concurrent Mode and Suspense to create an experience with a more fluid and app-like feel. Thanks to this project, we’re more confident than ever that Concurrent Mode and Suspense can make it easier to deliver great, fast user experiences. But doing so requires rethinking how we approach loading code and data for our apps. Effectively all of the data-fetching on the new facebook.com is powered by Relay Hooks — new Hooks-based Relay APIs that integrate with Concurrent Mode and Suspense out of the box. Relay Hooks — and GraphQL — won’t be for everyone, and that’s ok! Through our work on these APIs we’ve identified a set of more general patterns for using Suspense. Even if Relay isn’t the right fit for you, we think the key patterns we’ve introduced with Relay Hooks can be adapted to other frameworks. Best Practices for Suspense It’s tempting to focus only on the total startup time for an app — but it turns out that users’ perception of performance is determined by more than the absolute loading time. For example, when comparing two apps with the same absolute startup time, our research shows that users will generally perceive the one with fewer intermediate loading states and fewer layout changes as having loaded faster. Suspense is a powerful tool for carefully orchestrating an elegant loading sequence with a few, well-defined states that progressively reveal content. But improving perceived performance only goes so far — our apps still shouldn’t take forever to fetch all of their code, data, images, and other assets. The traditional approach to loading data in React apps involves what we refer to as “fetch-on-render”. First we render a component with a spinner, then fetch data on mount (componentDidMount or useEffect), and finally update to render the resulting data. It’s certainly possible to use this pattern with Suspense: instead of initially rendering a placeholder itself, a component can “suspend” — indicate to React that it isn’t ready yet. This will tell React to find the nearest ancestor <Suspense fallback={<Placeholder/>}>, and render its fallback instead. If you watched earlier Suspense demos this example may feel familiar — it’s how we originally imagined using Suspense for data-fetching. It turns out that this approach has some limitations. Consider a page that shows a social media post by a user, along with comments on that post. That might be structured as a <Post> component that renders both the post body and a <CommentList> to show the comments. Using the fetch-on-render approach described above to implement this could cause sequential round trips (sometimes referred to as a “waterfall”). First the data for the <Post> component would be fetched and then the data for <CommentList> would be fetched, increasing the time it takes to show the full page. There’s also another often-overlooked downside to this approach. If <Post> eagerly requires (or imports) the <CommentList> component, our app will have to wait to show the post body while the code for the comments is downloading. We could lazily load <CommentList>, but then that would delay fetching comments data and increase the time to show the full page. How do we resolve this problem without compromising on the user experience? Render As You Fetch The fetch-on-render approach is widely used by React apps today and can certainly be used to create great apps. But can we do even better? Let’s step back and consider our goal. In the above <Post> example, we’d ideally show the more important content — the post body — as early as possible, without negatively impacting the time to show the full page (including comments). Let’s consider the key constraints on any solution and look at how we can achieve them: Showing the more important content (the post body) as early as possible means that we need to load the code and data for the view incrementally. We don’t want to block showing the post body on the code for <CommentList> being downloaded, for example. At the same time we don’t want to increase the time to show the full page including comments. So we need to start loading the code and data for the comments as soon as possible, ideally in parallel with loading the post body. This might sound difficult to achieve — but these constraints are actually incredibly helpful. They rule out a large number of approaches and spell out a solution for us. This brings us to the key patterns we’ve implemented in Relay Hooks, and that can be adapted to other data-fetching libraries. We’ll look at each one in turn and then see how they add up to achieve our goal of fast, delightful loading experiences: Parallel data and view trees Fetch in event handlers Load data incrementally Treat code like data Parallel Data and View Trees One of the most appealing things about the fetch-on-render pattern is that it colocates what data a component needs with how to render that data. This colocation is great — an example of how it makes sense to group code by concerns and not by technologies. All the issues we saw above were due to when we fetch data in this approach: upon rendering. We need to be able to fetch data before we’ve rendered the component. The only way to achieve that is by extracting the data dependencies into parallel data and view trees. Here’s how that works in Relay Hooks. Continuing our example of a social media post with body and comments, here’s how we might define it with Relay Hooks: // Post.js function Post(props) { // Given a reference to some post - `props.post` - *what* data // do we need about that post? const postData = useFragment(graphql` fragment PostData on Post @refetchable(queryName: "PostQuery") { author title # ... more fields ... } `, props.post); // Now that we have the data, how do we render it? return ( <div> <h1>{postData.title}</h1> <h2>by {postData.author}</h2> {/* more fields */} </div> ); } Although the GraphQL is written within the component, Relay has a build step (Relay Compiler) that extracts these data-dependencies into separate files and aggregates the GraphQL for each view into a single query. So we get the benefit of colocating concerns, while at runtime having parallel data and view trees. Other frameworks could achieve a similar effect by allowing developers to define data-fetching logic in a sibling file (maybe Post.data.js), or perhaps integrate with a bundler to allow defining data dependencies with UI code and automatically extracting it, similar to Relay Compiler. The key is that regardless of the technology we’re using to load our data — GraphQL, REST, etc — we can separate what data to load from how and when to actually load it. But once we do that, how and when do we fetch our data? Fetch in Event Handlers Imagine that we’re about to navigate from a list of a user’s posts to the page for a specific post. We’ll need to download the code for that page — Post.js — and also fetch its data. Waiting until we render the component has problems as we saw above. The key is to start fetching code and data for a new view in the same event handler that triggers showing that view. We can either fetch the data within our router — if our router supports preloading data for routes — or in the click event on the link that triggered the navigation. It turns out that the React Router folks are already hard at work on building APIs to support preloading data for routes. But other routing frameworks can implement this idea too. Conceptually, we want every route definition to include two things: what component to render and what data to preload, as a function of the route/url params. Here’s what such a route definition might look like. This example is loosely inspired by React Router’s route definitions and is primarily intended to demonstrate the concept, not a specific API: // PostRoute.js (GraphQL version) // Relay generated query for loading Post data import PostQuery from './__generated__/PostQuery.graphql'; const PostRoute = { // a matching expression for which paths to handle path: '/post/:id', // what component to render for this route component: React.lazy(() => import('./Post')), // data to load for this route, as function of the route // parameters prepare: routeParams => { // Relay extracts queries from components, allowing us to reference // the data dependencies -- data tree -- from outside. const postData = preloadQuery(PostQuery, { postId: routeParams.id, }); return { postData }; }, }; export default PostRoute; Given such a definition, a router can: Match a URL to a route definition. Call the prepare() function to start loading that route’s data. Note that prepare() is synchronous — we don’t wait for the data to be ready, since we want to start rendering more important parts of the view (like the post body) as quickly as possible. Pass the preloaded data to the component. If the component is ready — the React.lazy dynamic import has completed — the component will render and try to access its data. If not, React.lazy will suspend until the code is ready. This approach can be generalized to other data-fetching solutions. An app that uses REST might define a route like this: // PostRoute.js (REST version) // Manually written logic for loading the data for the component import PostData from './Post.data'; const PostRoute = { // a matching expression for which paths to handle path: '/post/:id', // what component to render for this route component: React.lazy(() => import('./Post')), // data to load for this route, as function of the route // parameters prepare: routeParams => { const postData = preloadRestEndpoint( PostData.endpointUrl, { postId: routeParams.id, }, ); return { postData }; }, }; export default PostRoute; This same approach can be employed not just for routing, but in other places where we show content lazily or based on user interaction. For example, a tab component could eagerly load the first tab’s code and data, and then use the same pattern as above to load the code and data for other tabs in the tab-change event handler. A component that displays a modal could preload the code and data for the modal in the click handler that triggers opening the modal, and so on. Once we’ve implemented the ability to start loading code and data for a view independently, we have the option to go one step further. Consider a <Link to={path} /> component that links to a route. If the user hovers over that link, there’s a reasonable chance they’ll click it. And if they press the mouse down, there’s an even better chance that they’ll complete the click. If we can load code and data for a view after the user clicks, we can also start that work before they click, getting a head start on preparing the view. Best of all, we can centralize that logic in a few key places — a router or core UI components — and get any performance benefits automatically throughout our app. Of course preloading isn’t always beneficial. It’s something an application would tune based on the user’s device or network speed to avoid eating up user’s data plans. But the pattern here makes it easier to centralize the implementation of preloading and the decision of whether to enable it or not. Load Data Incrementally The above patterns — parallel data/view trees and fetching in event handlers — let us start loading all the data for a view earlier. But we still want to be able to show more important parts of the view without waiting for all of our data. At Facebook we’ve implemented support for this in GraphQL and Relay in the form of some new GraphQL directives (annotations that affect how/when data is delivered, but not what data). These new directives, called @defer and @stream, allow us to retrieve data incrementally. For example, consider our <Post> component from above. We want to show the body without waiting for the comments to be ready. We can achieve this with @defer and <Suspense>: // Post.js function Post(props) { const postData = useFragment(graphql` fragment PostData on Post { author title # fetch data for the comments, but don't block on it being ready ...CommentList @defer } `, props.post); return ( <div> <h1>{postData.title}</h1> <h2>by {postData.author}</h2> {/* @defer pairs naturally with <Suspense> to make the UI non-blocking too */} <Suspense fallback={<Spinner/>}> <CommentList post={postData} /> </Suspense> </div> ); } Here, our GraphQL server will stream back the results, first returning the author and title fields and then returning the comment data when it’s ready. We wrap <CommentList> in a <Suspense> boundary so that we can render the post body before <CommentList> and its data are ready. This same pattern can be applied to other frameworks as well. For example, apps that call a REST API might make parallel requests to fetch the body and comments data for a post to avoid blocking on all the data being ready. Treat Code Like Data But there’s one thing that’s still missing. We’ve shown how to preload data for a route — but what about code? The example above cheated a bit and used React.lazy. However, React.lazy is, as the name implies, lazy. It won’t start downloading code until the lazy component is actually rendered — it’s “fetch-on-render” for code! To solve this, the React team is considering APIs that would allow bundle splitting and eager preloading for code as well. That would allow a user to pass some form of lazy component to a router, and for the router to trigger loading the code alongside its data as early as possible. Putting It All Together To recap, achieving a great loading experience means that we need to start loading code and data as early as possible, but without waiting for all of it to be ready. Parallel data and view trees allow us to load the data for a view in parallel with loading the view (code) itself. Fetching in an event handler means we can start loading data as early as possible, and even optimistically preload a view when we have enough confidence that a user will navigate to it. Loading data incrementally allows us to load important data earlier without delaying the fetching of less important data. And treating code as data — and preloading it with similar APIs — allows us to load it earlier too. Using These Patterns These patterns aren’t just ideas — we’ve implemented them in Relay Hooks and are using them in production throughout the new facebook.com (which is currently in beta testing). If you’re interested in using or learning more about these patterns, here are some resources: The React Concurrent docs explore how to use Concurrent Mode and Suspense and go into more detail about many of these patterns. It’s a great resource to learn more about the APIs and use-cases they support. The experimental release of Relay Hooks implements the patterns described here. We’ve implemented two similar example apps that demonstrate these concepts: The Relay Hooks example app uses GitHub’s public GraphQL API to implement a simple issue tracker app. It includes nested route support with code and data preloading. The code is fully commented — we encourage cloning the repo, running the app locally, and exploring how it works. We also have a non-GraphQL version of the app that demonstrates how these concepts can be applied to other data-fetching libraries. While the APIs around Concurrent Mode and Suspense are still experimental, we’re confident that the ideas in this post are proven by practice. However, we understand that Relay and GraphQL aren’t the right fit for everyone. That’s ok! We’re actively exploring how to generalize these patterns to approaches such as REST, and are exploring ideas for a more generic (ie non-GraphQL) API for composing a tree of data dependencies. In the meantime, we’re excited to see what new libraries will emerge that implement the patterns described in this post to make it easier to build great, fast user experiences.

Preparing for the Future with React Prereleases

To share upcoming changes with our partners in the React ecosystem, we’re establishing official prerelease channels. We hope this process will help us make changes to React with confidence, and give developers the opportunity to try out experimental features. This post will be most relevant to developers who work on frameworks, libraries, or developer tooling. Developers who use React primarily to build user-facing applications should not need to worry about our prerelease channels. React relies on a thriving open source community to file bug reports, open pull requests, and submit RFCs. To encourage feedback, we sometimes share special builds of React that include unreleased features. Because the source of truth for React is our public GitHub repository, it’s always been possible to build a copy of React that includes the latest changes. However it’s much easier for developers to install React from npm, so we occasionally publish prerelease builds to the npm registry. A recent example is the 16.7 alpha, which included an early version of the Hooks API. We would like to make it even easier for developers to test prerelease builds of React, so we’re formalizing our process with three separate release channels. Release Channels The information in this post is also available on our Release Channels page. We will update that document whenever there are changes to our release process. Each of React’s release channels is designed for a distinct use case: Latest is for stable, semver React releases. It’s what you get when you install React from npm. This is the channel you’re already using today. Use this for all user-facing React applications. Next tracks the master branch of the React source code repository. Think of these as release candidates for the next minor semver release. Use this for integration testing between React and third party projects. Experimental includes experimental APIs and features that aren’t available in the stable releases. These also track the master branch, but with additional feature flags turned on. Use this to try out upcoming features before they are released. All releases are published to npm, but only Latest uses semantic versioning. Prereleases (those in the Next and Experimental channels) have versions generated from a hash of their contents, e.g. 0.0.0-1022ee0ec for Next and 0.0.0-experimental-1022ee0ec for Experimental. The only officially supported release channel for user-facing applications is Latest. Next and Experimental releases are provided for testing purposes only, and we provide no guarantees that behavior won’t change between releases. They do not follow the semver protocol that we use for releases from Latest. By publishing prereleases to the same registry that we use for stable releases, we are able to take advantage of the many tools that support the npm workflow, like unpkg and CodeSandbox. Latest Channel Latest is the channel used for stable React releases. It corresponds to the latest tag on npm. It is the recommended channel for all React apps that are shipped to real users. If you’re not sure which channel you should use, it’s Latest. If you’re a React developer, this is what you’re already using. You can expect updates to Latest to be extremely stable. Versions follow the semantic versioning scheme. Learn more about our commitment to stability and incremental migration in our versioning policy. Next Channel The Next channel is a prerelease channel that tracks the master branch of the React repository. We use prereleases in the Next channel as release candidates for the Latest channel. You can think of Next as a superset of Latest that is updated more frequently. The degree of change between the most recent Next release and the most recent Latest release is approximately the same as you would find between two minor semver releases. However, the Next channel does not conform to semantic versioning. You should expect occasional breaking changes between successive releases in the Next channel. Do not use prereleases in user-facing applications. Releases in Next are published with the next tag on npm. Versions are generated from a hash of the build’s contents, e.g. 0.0.0-1022ee0ec. Using the Next Channel for Integration Testing The Next channel is designed to support integration testing between React and other projects. All changes to React go through extensive internal testing before they are released to the public. However, there are myriad environments and configurations used throughout the React ecosystem, and it’s not possible for us to test against every single one. If you’re the author of a third party React framework, library, developer tool, or similar infrastructure-type project, you can help us keep React stable for your users and the entire React community by periodically running your test suite against the most recent changes. If you’re interested, follow these steps: Set up a cron job using your preferred continuous integration platform. Cron jobs are supported by both CircleCI and Travis CI. In the cron job, update your React packages to the most recent React release in the Next channel, using next tag on npm. Using the npm cli: npm update react@next react-dom@next Or yarn: yarn upgrade react@next react-dom@next Run your test suite against the updated packages. If everything passes, great! You can expect that your project will work with the next minor React release. If something breaks unexpectedly, please let us know by filing an issue. A project that uses this workflow is Next.js. (No pun intended! Seriously!) You can refer to their CircleCI configuration as an example. Experimental Channel Like Next, the Experimental channel is a prerelease channel that tracks the master branch of the React repository. Unlike Next, Experimental releases include additional features and APIs that are not ready for wider release. Usually, an update to Next is accompanied by a corresponding update to Experimental. They are based on the same source revision, but are built using a different set of feature flags. Experimental releases may be significantly different than releases to Next and Latest. Do not use Experimental releases in user-facing applications. You should expect frequent breaking changes between releases in the Experimental channel. Releases in Experimental are published with the experimental tag on npm. Versions are generated from a hash of the build’s contents, e.g. 0.0.0-experimental-1022ee0ec. What Goes Into an Experimental Release? Experimental features are ones that are not ready to be released to the wider public, and may change drastically before they are finalized. Some experiments may never be finalized — the reason we have experiments is to test the viability of proposed changes. For example, if the Experimental channel had existed when we announced Hooks, we would have released Hooks to the Experimental channel weeks before they were available in Latest. You may find it valuable to run integration tests against Experimental. This is up to you. However, be advised that Experimental is even less stable than Next. We do not guarantee any stability between Experimental releases. How Can I Learn More About Experimental Features? Experimental features may or may not be documented. Usually, experiments aren’t documented until they are close to shipping in Next or Stable. If a feature is not documented, they may be accompanied by an RFC. We will post to the React blog when we’re ready to announce new experiments, but that doesn’t mean we will publicize every experiment. You can always refer to our public GitHub repository’s history for a comprehensive list of changes.

Introducing the New React DevTools

We are excited to announce a new release of the React Developer Tools, available today in Chrome, Firefox, and (Chromium) Edge! What’s changed? A lot has changed in version 4! At a high level, this new version should offer significant performance gains and an improved navigation experience. It also offers full support for React Hooks, including inspecting nested objects. Visit the interactive tutorial to try out the new version or see the changelog for demo videos and more details. Which versions of React are supported? react-dom 0-14.x: Not supported 15.x: Supported (except for the new component filters feature) 16.x: Supported react-native 0-0.61.x: Not supported 0.62: Supported How do I get the new DevTools? React DevTools is available as an extension for Chrome and Firefox. If you have already installed the extension, it should update automatically within the next couple of hours. If you use the standalone shell (e.g. in React Native or Safari), you can install the new version from NPM: npm install -g react-devtools@^4 Where did all of the DOM elements go? The new DevTools provides a way to filter components from the tree to make it easier to navigate deeply nested hierarchies. Host nodes (e.g. HTML <div>, React Native <View>) are hidden by default, but this filter can be disabled: How do I get the old version back? If you are working with React Native version 60 (or older) you can install the previous release of DevTools from NPM: npm install --dev react-devtools@^3 For older versions of React DOM (v0.14 or earlier) you will need to build the extension from source: # Checkout the extension source git clone https://github.com/facebook/react-devtools cd react-devtools # Checkout the previous release branch git checkout v3 # Install dependencies and build the unpacked extension yarn install yarn build:extension # Follow the on-screen instructions to complete installation Thank you! We’d like to thank everyone who tested the early release of DevTools version 4. Your feedback helped improve this initial release significantly. We still have many exciting features planned and feedback is always welcome! Please feel free to open a GitHub issue or tag @reactjs on Twitter.

React v16.9.0 and the Roadmap Update

Today we are releasing React 16.9. It contains several new features, bugfixes, and new deprecation warnings to help prepare for a future major release. New Deprecations Renaming Unsafe Lifecycle Methods Over a year ago, we announced that unsafe lifecycle methods are getting renamed: componentWillMount → UNSAFE_componentWillMount componentWillReceiveProps → UNSAFE_componentWillReceiveProps componentWillUpdate → UNSAFE_componentWillUpdate React 16.9 does not contain breaking changes, and the old names continue to work in this release. But you will now see a warning when using any of the old names: As the warning suggests, there are usually better approaches for each of the unsafe methods. However, maybe you don’t have the time to migrate or test these components. In that case, we recommend running a “codemod” script that renames them automatically: cd your_project npx react-codemod rename-unsafe-lifecycles (Note that it says npx, not npm. npx is a utility that comes with Node 6+ by default.) Running this codemod will replace the old names like componentWillMount with the new names like UNSAFE_componentWillMount: The new names like UNSAFE_componentWillMount will keep working in both React 16.9 and in React 17.x. However, the new UNSAFE_ prefix will help components with problematic patterns stand out during the code review and debugging sessions. (If you’d like, you can further discourage their use inside your app with the opt-in Strict Mode.) Note Learn more about our versioning policy and commitment to stability. Deprecating javascript: URLs URLs starting with javascript: are a dangerous attack surface because it’s easy to accidentally include unsanitized output in a tag like <a href> and create a security hole: const userProfile = { website: "javascript: alert('you got hacked')", }; // This will now warn: <a href={userProfile.website}>Profile</a> In React 16.9, this pattern continues to work, but it will log a warning. If you use javascript: URLs for logic, try to use React event handlers instead. (As a last resort, you can circumvent the protection with dangerouslySetInnerHTML, but it is highly discouraged and often leads to security holes.) In a future major release, React will throw an error if it encounters a javascript: URL. Deprecating “Factory” Components Before compiling JavaScript classes with Babel became popular, React had support for a “factory” component that returns an object with a render method: function FactoryComponent() { return { render() { return <div />; } } } This pattern is confusing because it looks too much like a function component — but it isn’t one. (A function component would just return the <div /> in the above example.) This pattern was almost never used in the wild, and supporting it causes React to be slightly larger and slower than necessary. So we are deprecating this pattern in 16.9 and logging a warning if it’s encountered. If you rely on it, adding FactoryComponent.prototype = React.Component.prototype can serve as a workaround. Alternatively, you can convert it to either a class or a function component. We don’t expect most codebases to be affected by this. New Features Async act() for Testing React 16.8 introduced a new testing utility called act() to help you write tests that better match the browser behavior. For example, multiple state updates inside a single act() get batched. This matches how React already works when handling real browser events, and helps prepare your components for the future in which React will batch updates more often. However, in 16.8 act() only supported synchronous functions. Sometimes, you might have seen a warning like this in a test but could not easily fix it: An update to SomeComponent inside a test was not wrapped in act(...). In React 16.9, act() also accepts asynchronous functions, and you can await its call: await act(async () => { // ... }); This solves the remaining cases where you couldn’t use act() before, such as when the state update was inside an asynchronous function. As a result, you should be able to fix all the remaining act() warnings in your tests now. We’ve heard there wasn’t enough information about how to write tests with act(). The new Testing Recipes guide describes common scenarios, and how act() can help you write good tests. These examples use vanilla DOM APIs, but you can also use React Testing Library to reduce the boilerplate code. Many of its methods already use act() internally. Please let us know on the issue tracker if you bump into any other scenarios where act() doesn’t work well for you, and we’ll try to help. Performance Measurements with <React.Profiler> In React 16.5, we introduced a new React Profiler for DevTools that helps find performance bottlenecks in your application. In React 16.9, we are also adding a programmatic way to gather measurements called <React.Profiler>. We expect that most smaller apps won’t use it, but it can be handy to track performance regressions over time in larger apps. The <Profiler> measures how often a React application renders and what the “cost” of rendering is. Its purpose is to help identify parts of an application that are slow and may benefit from optimizations such as memoization. A <Profiler> can be added anywhere in a React tree to measure the cost of rendering that part of the tree. It requires two props: an id (string) and an onRender callback (function) which React calls any time a component within the tree “commits” an update. render( <Profiler id="application" onRender={onRenderCallback}> <App> <Navigation {...props} /> <Main {...props} /> </App> </Profiler>); To learn more about the Profiler and the parameters passed to the onRender callback, check out the Profiler docs. Note: Profiling adds some additional overhead, so it is disabled in the production build. To opt into production profiling, React provides a special production build with profiling enabled. Read more about how to use this build at fb.me/react-profiling. Notable Bugfixes This release contains a few other notable improvements: A crash when calling findDOMNode() inside a <Suspense> tree has been fixed. A memory leak caused by retaining deleted subtrees has been fixed too. An infinite loop caused by setState in useEffect now logs an error. (This is similar to the error you see when you call setState in componentDidUpdate in a class.) We’re thankful to all the contributors who helped surface and fix these and other issues. You can find the full changelog below. An Update to the Roadmap In November 2018, we have posted this roadmap for the 16.x releases: A minor 16.x release with React Hooks (past estimate: Q1 2019) A minor 16.x release with Concurrent Mode (past estimate: Q2 2019) A minor 16.x release with Suspense for Data Fetching (past estimate: mid 2019) These estimates were too optimistic, and we’ve needed to adjust them. tldr: We shipped Hooks on time, but we’re regrouping Concurrent Mode and Suspense for Data Fetching into a single release that we intend to release later this year. In February, we shipped a stable 16.8 release including React Hooks, with React Native support coming a month later. However, we underestimated the follow-up work for this release, including the lint rules, developer tools, examples, and more documentation. This shifted the timeline by a few months. Now that React Hooks are rolled out, the work on Concurrent Mode and Suspense for Data Fetching is in full swing. The new Facebook website that’s currently in active development is built on top of these features. Testing them with real code helped discover and address many issues before they can affect the open source users. Some of these fixes involved an internal redesign of these features, which has also caused the timeline to slip. With this new understanding, here’s what we plan to do next. One Release Instead of Two Concurrent Mode and Suspense power the new Facebook website that’s in active development, so we are confident that they’re close to a stable state technically. We also now better understand the concrete steps before they are ready for open source adoption. Originally we thought we would split Concurrent Mode and Suspense for Data Fetching into two releases. We’ve found that this sequencing is confusing to explain because these features are more related than we thought at first. So we plan to release support for both Concurrent Mode and Suspense for Data Fetching in a single combined release instead. We don’t want to overpromise the release date again. Given that we rely on both of them in production code, we expect to provide a 16.x release with opt-in support for them this year. An Update on Data Fetching While React is not opinionated about how you fetch data, the first release of Suspense for Data Fetching will likely focus on integrating with opinionated data fetching libraries. For example, at Facebook we are using upcoming Relay APIs that integrate with Suspense. We will document how other opinionated libraries like Apollo can support a similar integration. In the first release, we don’t intend to focus on the ad-hoc “fire an HTTP request” solution we used in earlier demos (also known as “React Cache”). However, we expect that both we and the React community will be exploring that space in the months after the initial release. An Update on Server Rendering We have started the work on the new Suspense-capable server renderer, but we don’t expect it to be ready for the initial release of Concurrent Mode. This release will, however, provide a temporary solution that lets the existing server renderer emit HTML for Suspense fallbacks immediately, and then render their real content on the client. This is the solution we are currently using at Facebook ourselves until the streaming renderer is ready. Why Is It Taking So Long? We’ve shipped the individual pieces leading up to Concurrent Mode as they became stable, including new context API, lazy loading with Suspense, and Hooks. We are also eager to release the other missing parts, but trying them at scale is an important part of the process. The honest answer is that it just took more work than we expected when we started. As always, we appreciate your questions and feedback on Twitter and in our issue tracker. Installation React React v16.9.0 is available on the npm registry. To install React 16 with Yarn, run: yarn add react@^16.9.0 react-dom@^16.9.0 To install React 16 with npm, run: npm install --save react@^16.9.0 react-dom@^16.9.0 We also provide UMD builds of React via a CDN: <script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script> Refer to the documentation for detailed installation instructions. Changelog React Add <React.Profiler> API for gathering performance measurements programmatically. (@bvaughn in #15172) Remove unstable_ConcurrentMode in favor of unstable_createRoot. (@acdlite in #15532) React DOM Deprecate old names for the UNSAFE_* lifecycle methods. (@bvaughn in #15186 and @threepointone in #16103) Deprecate javascript: URLs as a common attack surface. (@sebmarkbage in #15047) Deprecate uncommon “module pattern” (factory) components. (@sebmarkbage in #15145) Add support for the disablePictureInPicture attribute on <video>. (@eek in #15334) Add support for onLoad event for <embed>. (@cherniavskii in #15614) Add support for editing useState state from DevTools. (@bvaughn in #14906) Add support for toggling Suspense from DevTools. (@gaearon in #15232) Warn when setState is called from useEffect, creating a loop. (@gaearon in #15180) Fix a memory leak. (@paulshen in #16115) Fix a crash inside findDOMNode for components wrapped in <Suspense>. (@acdlite in #15312) Fix pending effects from being flushed too late. (@acdlite in #15650) Fix incorrect argument order in a warning message. (@brickspert in #15345) Fix hiding Suspense fallback nodes when there is an !important style. (@acdlite in #15861 and #15882) Slightly improve hydration performance. (@bmeurer in #15998) React DOM Server Fix incorrect output for camelCase custom CSS property names. (@bedakb in #16167) React Test Utilities and Test Renderer Add act(async () => ...) for testing asynchronous state updates. (@threepointone in #14853) Add support for nesting act from different renderers. (@threepointone in #16039 and #16042) Warn in Strict Mode if effects are scheduled outside an act() call. (@threepointone in #15763 and #16041) Warn when using act from the wrong renderer. (@threepointone in #15756)

Is React Translated Yet? ¡Sí! Sim! はい!

We’re excited to announce an ongoing effort to maintain official translations of the React documentation website into different languages. Thanks to the dedicated efforts of React community members from around the world, React is now being translated into over 30 languages! You can find them on the new Languages page. In addition, the following three languages have completed translating most of the React Docs! 🎉 Spanish: es.reactjs.org Japanese: ja.reactjs.org Brazilian Portuguese: pt-br.reactjs.org Special congratulations to Alejandro Ñáñez Ortiz, Rainer Martínez Fraga, David Morales, Miguel Alejandro Bolivar Portilla, and all the contributors to the Spanish translation for being the first to completely translate the core pages of the docs! Why Localization Matters React already has many meetups and conferences around the world, but many programmers don’t use English as their primary language. We’d love to support local communities who use React by making our documentation available in most popular languages. In the past, React community members have created unofficial translations for Chinese, Arabic, and Korean; by making an official channel for these translated docs we’re hoping to make them easier to find and help make sure that non-English-speaking users of React aren’t left behind. Contributing If you would like to help out on a current translation, check out the Languages page and click on the “Contribute” link for your language. Can’t find your language? If you’d like to maintain your language’s translation fork, follow the instructions in the translation repo! Backstory Hi everyone! I’m Nat! You may know me as the polyhedra lady. For the past few weeks, I’ve been helping the React team coordinate their translation effort. Here’s how I did it. Our original approach for translations was to use a SaaS platform that allows users to submit translations. There was already a pull request to integrate it and my original responsibility was to finish that integration. However, we had concerns about the feasibility of that integration and the current quality of translations on the platform. Our primary concern was ensuring that translations kept up to date with the main repo and didn’t become “stale”. Dan encouraged me to look for alternate solutions, and we stumbled across how Vue maintained its translations — through different forks of the main repo on GitHub. In particular, the Japanese translation used a bot to periodically check for changes in the English repo and submits pull requests whenever there is a change. This approach appealed to us for several reasons: It was less code integration to get off the ground. It encouraged active maintainers for each repo to ensure quality. Contributors already understand GitHub as a platform and are motivated to contribute directly to the React organization. We started off with an initial trial period of three languages: Spanish, Japanese, and Simplified Chinese. This allowed us to work out any kinks in our process and make sure future translations are set up for success. I wanted to give the translation teams freedom to choose whatever tools they felt comfortable with. The only requirement is a checklist that outlines the order of importance for translating pages. After the trial period, we were ready to accept more languages. I created a script to automate the creation of the new language repo, and a site, Is React Translated Yet?, to track progress on the different translations. We started 10 new translations on our first day alone! Because of the automation, the rest of the maintenance went mostly smoothly. We eventually created a Slack channel to make it easier for translators to share information, and I released a guide solidifying the responsibilities of maintainers. Allowing translators to talk with each other was a great boon — for example, the Arabic, Persian, and Hebrew translations were able to talk to each other in order to get right-to-left text working! The Bot The most challenging part was getting the bot to sync changes from the English version of the site. Initially we were using the che-tsumi bot created by the Japanese Vue translation team, but we soon decided to build our own bot to suit our needs. In particular, the che-tsumi bot works by cherry picking new commits. This ended up causing a cavalade of new issues that were interconnected, especially when Hooks were released. In the end, we decided that instead of cherry picking each commit, it made more sense to merge all new commits and create a pull request around once a day. Conflicts are merged as-is and listed in the pull request, leaving a checklist for maintainers to fix. Creating the sync script was easy enough: it downloads the translated repo, adds the original as a remote, pulls from it, merges the conflicts, and creates a pull request. The problem was finding a place for the bot to run. I’m a frontend developer for a reason — Heroku and its ilk are alien to me and endlessly frustrating. In fact, until this past Tuesday, I was running the script by hand on my local machine! The biggest challenge was space. Each fork of the repo is around 100MB — which takes minutes to clone on my local machine. We have 32 forks, and the free tiers of most deployment platforms I checked limited you to 512MB of storage. After lots of notepad calculations, I found a solution: delete each repo once we’ve finished the script and limit the concurrency of sync scripts that run at once to be within the storage requirements. Luckily, Heroku dynos have a much faster Internet connection and are able to clone even the React repo quickly. There were other smaller issues that I ran into. I tried using the Heroku Scheduler add-on so I didn’t have to write any actual watch code, but it end up running too inconsistently, and I had an existential meltdown on Twitter when I couldn’t figure out how to send commits from the Heroku dyno. But in the end, this frontend engineer was able to get the bot working! There are, as always, improvements I want to make to the bot. Right now it doesn’t check whether there is an outstanding pull request before pushing another one. It’s still hard to tell the exact change that happened in the original source, and it’s possible to miss out on a needed translation change. But I trust the maintainers we’ve chosen to work through these issues, and the bot is open source if anyone wants to help me make these improvements! Thanks Finally, I would like to extend my gratitude to the following people and groups: All the translation maintainers and contributors who are helping translate React to more than thirty languages. The Vue.js Japan User Group for initiating the idea of having bot-managed translations, and especially Hanatani Takuma for helping us understand their approach and helping maintain the Japanese translation. Soichiro Miki for many contributions and thoughtful comments on the overall translation process, as well as for maintaining the Japanese translation. Eric Nakagawa for managing our previous translation process. Brian Vaughn for setting up the languages page and managing all the subdomains. And finally, thank you to Dan Abramov for giving me this opportunity and being a great mentor along the way.