By default, these animations use the default CSS animations for View Transitions applied (typically a smooth cross-fade). You can use view transition pseudo-selectors to define “how” the animation runs. For example, you can use *
to change the default animation for all transitions:
// \\"how\\" to animate.
::view-transition-old(*) {
animation: 300ms ease-out fade-out;
}
::view-transition-new(*) {
animation: 300ms ease-in fade-in;
}
When the DOM updates due to an animation trigger—like startTransition
, useDeferredValue
, or a Suspense
fallback switching to content—React will use declarative heuristics to automatically determine which <ViewTransition>
components to activate for the animation. The browser will then run the animation that’s defined in CSS.
If you’re familiar with the browser’s View Transition API and want to know how React supports it, check out How does <ViewTransition>
Work in the docs.
In this post, let’s take a look at a few examples of how to use View Transitions.
\\nWe’ll start with this app, which doesn’t animate any of the following interactions:
\\nimport TalkDetails from \'./Details\'; import Home from \'./Home\'; import {useRouter} from \'./router\';\\n\\nexport default function App() {\\n const {url} = useRouter();\\n\\n // 🚩This version doesn\'t include any animations yet\\n return url === \'/\' ? <Home /> : <TalkDetails />;\\n}\\n\\n
View Transitions are meant to be used for UI transitions such as navigation, expanding, opening, or re-ordering. They are not meant to replace all the animations in your app.
In our example app above, notice that there are already animations when you click the “like” button and the Suspense fallback glimmer. These are good use cases for CSS animations because they are animating a specific element.
Our app includes a Suspense-enabled router, with page transitions already marked as Transitions, which means navigations are performed with startTransition
:
function navigate(url) {
startTransition(() => {
go(url);
});
}
startTransition
is a View Transition trigger, so we can add <ViewTransition>
to animate between pages:
// \\"what\\" to animate
<ViewTransition key={url}>
{url === \'/\' ? <Home /> : <TalkDetails />}
</ViewTransition>
When the url
changes, the <ViewTransition>
and new route are rendered. Since the <ViewTransition>
was updated inside of startTransition
, the <ViewTransition>
is activated for an animation.
By default, View Transitions include the browser default cross-fade animation. Adding this to our example, we now have a cross-fade whenever we navigate between pages:
\\nimport {unstable_ViewTransition as ViewTransition} from \'react\'; import Details from \'./Details\'; import Home from \'./Home\'; import {useRouter} from \'./router\';\\n\\nexport default function App() {\\n const {url} = useRouter();\\n \\n // Use ViewTransition to animate between pages.\\n // No additional CSS needed by default.\\n return (\\n <ViewTransition>\\n {url === \'/\' ? <Home /> : <Details />}\\n </ViewTransition>\\n );\\n}\\n\\n
Since our router already updates the route using startTransition
, this one line change to add <ViewTransition>
activates with the default cross-fade animation.
If you’re curious how this works, see the docs for How does <ViewTransition>
work?
<ViewTransition>
animations In this example, we’re wrapping the root of the app in <ViewTransition>
for simplicity, but this means that all transitions in the app will be animated, which can lead to unexpected animations.
To fix, we’re wrapping route children with \\"none\\"
so each page can control it’s own animation:
// Layout.js
<ViewTransition default=\\"none\\">
{children}
</ViewTransition>
In practice, navigations should be done via “enter” and “exit” props, or by using Transition Types.
By default, <ViewTransition>
includes the default cross-fade from the browser.
To customize animations, you can provide props to the <ViewTransition>
component to specify which animations to use, based on how the <ViewTransition>
activates.
For example, we can slow down the default
cross fade animation:
<ViewTransition default=\\"slow-fade\\">
<Home />
</ViewTransition>
And define slow-fade
in CSS using view transition classes:
::view-transition-old(.slow-fade) {
animation-duration: 500ms;
}
::view-transition-new(.slow-fade) {
animation-duration: 500ms;
}
Now, the cross fade is slower:
\\nimport { unstable_ViewTransition as ViewTransition } from \\"react\\";\\nimport Details from \\"./Details\\";\\nimport Home from \\"./Home\\";\\nimport { useRouter } from \\"./router\\";\\n\\nexport default function App() {\\n const { url } = useRouter();\\n\\n // Define a default animation of .slow-fade.\\n // See animations.css for the animation definiton.\\n return (\\n <ViewTransition default=\\"slow-fade\\">\\n {url === \'/\' ? <Home /> : <Details />}\\n </ViewTransition>\\n );\\n}\\n\\n
See Styling View Transitions for a full guide on styling <ViewTransition>
.
When two pages include the same element, often you want to animate it from one page to the next.
\\nTo do this you can add a unique name
to the <ViewTransition>
:
<ViewTransition name={`video-${video.id}`}>
<Thumbnail video={video} />
</ViewTransition>
Now the video thumbnail animates between the two pages:
\\nimport { useState, unstable_ViewTransition as ViewTransition } from \\"react\\"; import LikeButton from \\"./LikeButton\\"; import { useRouter } from \\"./router\\"; import { PauseIcon, PlayIcon } from \\"./Icons\\"; import { startTransition } from \\"react\\";\\n\\nexport function Thumbnail({ video, children }) {\\n // Add a name to animate with a shared element transition.\\n // This uses the default animation, no additional css needed.\\n return (\\n <ViewTransition name={`video-${video.id}`}>\\n <div\\n aria-hidden=\\"true\\"\\n tabIndex={-1}\\n className={`thumbnail ${video.image}`}\\n >\\n {children}\\n </div>\\n </ViewTransition>\\n );\\n}\\n\\nexport function VideoControls() {\\n const [isPlaying, setIsPlaying] = useState(false);\\n\\n return (\\n <span\\n className=\\"controls\\"\\n onClick={() =>\\n startTransition(() => {\\n setIsPlaying((p) => !p);\\n })\\n }\\n >\\n {isPlaying ? <PauseIcon /> : <PlayIcon />}\\n </span>\\n );\\n}\\n\\nexport function Video({ video }) {\\n const { navigate } = useRouter();\\n\\n return (\\n <div className=\\"video\\">\\n <div\\n className=\\"link\\"\\n onClick={(e) => {\\n e.preventDefault();\\n navigate(`/video/${video.id}`);\\n }}\\n >\\n <Thumbnail video={video}></Thumbnail>\\n\\n <div className=\\"info\\">\\n <div className=\\"video-title\\">{video.title}</div>\\n <div className=\\"video-description\\">{video.description}</div>\\n </div>\\n </div>\\n <LikeButton video={video} />\\n </div>\\n );\\n}\\n\\n
By default, React automatically generates a unique name
for each element activated for a transition (see How does <ViewTransition>
work). When React sees a transition where a <ViewTransition>
with a name
is removed and a new <ViewTransition>
with the same name
is added, it will activate a shared element transition.
For more info, see the docs for Animating a Shared Element.
\\nSometimes, you may want elements to animate differently based on how it was triggered. For this use case, we’ve added a new API called addTransitionType
to specify the cause of a transition:
function navigate(url) {
startTransition(() => {
// Transition type for the cause \\"nav forward\\"
addTransitionType(\'nav-forward\');
go(url);
});
}
function navigateBack(url) {
startTransition(() => {
// Transition type for the cause \\"nav backward\\"
addTransitionType(\'nav-back\');
go(url);
});
}
With transition types, you can provide custom animations via props to <ViewTransition>
. Let’s add a shared element transition to the header for “6 Videos” and “Back”:
<ViewTransition
name=\\"nav\\"
share={{
\'nav-forward\': \'slide-forward\',
\'nav-back\': \'slide-back\',
}}>
{heading}
</ViewTransition>
Here we pass a share
prop to define how to animate based on the transition type. When the share transition activates from nav-forward
, the view transition class slide-forward
is applied. When it’s from nav-back
, the slide-back
animation is activated. Let’s define these animations in CSS:
::view-transition-old(.slide-forward) {
/* when sliding forward, the \\"old\\" page should slide out to left. */
animation: ...
}
::view-transition-new(.slide-forward) {
/* when sliding forward, the \\"new\\" page should slide in from right. */
animation: ...
}
::view-transition-old(.slide-back) {
/* when sliding back, the \\"old\\" page should slide out to right. */
animation: ...
}
::view-transition-new(.slide-back) {
/* when sliding back, the \\"new\\" page should slide in from left. */
animation: ...
}
Now we can animate the header along with thumbnail based on navigation type:
\\nimport {unstable_ViewTransition as ViewTransition} from \'react\'; import { useIsNavPending } from \\"./router\\";\\n\\nexport default function Page({ heading, children }) {\\n const isPending = useIsNavPending();\\n return (\\n <div className=\\"page\\">\\n <div className=\\"top\\">\\n <div className=\\"top-nav\\">\\n {/* Custom classes based on transition type. */}\\n <ViewTransition\\n name=\\"nav\\"\\n share={{\\n \'nav-forward\': \'slide-forward\',\\n \'nav-back\': \'slide-back\',\\n }}>\\n {heading}\\n </ViewTransition>\\n {isPending && <span className=\\"loader\\"></span>}\\n </div>\\n </div>\\n {/* Opt-out of ViewTransition for the content. */}\\n {/* Content can define it\'s own ViewTransition. */}\\n <ViewTransition default=\\"none\\">\\n <div className=\\"bottom\\">\\n <div className=\\"content\\">{children}</div>\\n </div>\\n </ViewTransition>\\n </div>\\n );\\n}\\n\\n
Suspense will also activate View Transitions.
\\nTo animate the fallback to content, we can wrap Suspense
with <ViewTranstion>
:
<ViewTransition>
<Suspense fallback={<VideoInfoFallback />}>
<VideoInfo />
</Suspense>
</ViewTransition>
By adding this, the fallback will cross-fade into the content. Click a video and see the video info animate in:
\\nimport { use, Suspense, unstable_ViewTransition as ViewTransition } from \\"react\\"; import { fetchVideo, fetchVideoDetails } from \\"./data\\"; import { Thumbnail, VideoControls } from \\"./Videos\\"; import { useRouter } from \\"./router\\"; import Layout from \\"./Layout\\"; import { ChevronLeft } from \\"./Icons\\";\\n\\nfunction VideoDetails({ id }) {\\n // Cross-fade the fallback to content.\\n return (\\n <ViewTransition default=\\"slow-fade\\">\\n <Suspense fallback={<VideoInfoFallback />}>\\n <VideoInfo id={id} />\\n </Suspense>\\n </ViewTransition>\\n );\\n}\\n\\nfunction VideoInfoFallback() {\\n return (\\n <div>\\n <div className=\\"fit fallback title\\"></div>\\n <div className=\\"fit fallback description\\"></div>\\n </div>\\n );\\n}\\n\\nexport default function Details() {\\n const { url, navigateBack } = useRouter();\\n const videoId = url.split(\\"/\\").pop();\\n const video = use(fetchVideo(videoId));\\n\\n return (\\n <Layout\\n heading={\\n <div\\n className=\\"fit back\\"\\n onClick={() => {\\n navigateBack(\\"/\\");\\n }}\\n >\\n <ChevronLeft /> Back\\n </div>\\n }\\n >\\n <div className=\\"details\\">\\n <Thumbnail video={video} large>\\n <VideoControls />\\n </Thumbnail>\\n <VideoDetails id={video.id} />\\n </div>\\n </Layout>\\n );\\n}\\n\\nfunction VideoInfo({ id }) {\\n const details = use(fetchVideoDetails(id));\\n return (\\n <div>\\n <p className=\\"fit info-title\\">{details.title}</p>\\n <p className=\\"fit info-description\\">{details.description}</p>\\n </div>\\n );\\n}\\n\\n
We can also provide custom animatons using an exit
on the fallback, and enter
on the content:
<Suspense
fallback={
<ViewTransition exit=\\"slide-down\\">
<VideoInfoFallback />
</ViewTransition>
}
>
<ViewTransition enter=\\"slide-up\\">
<VideoInfo id={id} />
</ViewTransition>
</Suspense>
Here’s how we’ll define slide-down
and slide-up
with CSS:
::view-transition-old(.slide-down) {
/* Slide the fallback down */
animation: ...;
}
::view-transition-new(.slide-up) {
/* Slide the content up */
animation: ...;
}
Now, the Suspense content replaces the fallback with a sliding animation:
\\nimport { use, Suspense, unstable_ViewTransition as ViewTransition } from \\"react\\"; import { fetchVideo, fetchVideoDetails } from \\"./data\\"; import { Thumbnail, VideoControls } from \\"./Videos\\"; import { useRouter } from \\"./router\\"; import Layout from \\"./Layout\\"; import { ChevronLeft } from \\"./Icons\\";\\n\\nfunction VideoDetails({ id }) {\\n return (\\n <Suspense\\n fallback={\\n // Animate the fallback down.\\n <ViewTransition exit=\\"slide-down\\">\\n <VideoInfoFallback />\\n </ViewTransition>\\n }\\n >\\n {/* Animate the content up */}\\n <ViewTransition enter=\\"slide-up\\">\\n <VideoInfo id={id} />\\n </ViewTransition>\\n </Suspense>\\n );\\n}\\n\\nfunction VideoInfoFallback() {\\n return (\\n <>\\n <div className=\\"fallback title\\"></div>\\n <div className=\\"fallback description\\"></div>\\n </>\\n );\\n}\\n\\nexport default function Details() {\\n const { url, navigateBack } = useRouter();\\n const videoId = url.split(\\"/\\").pop();\\n const video = use(fetchVideo(videoId));\\n\\n return (\\n <Layout\\n heading={\\n <div\\n className=\\"fit back\\"\\n onClick={() => {\\n navigateBack(\\"/\\");\\n }}\\n >\\n <ChevronLeft /> Back\\n </div>\\n }\\n >\\n <div className=\\"details\\">\\n <Thumbnail video={video} large>\\n <VideoControls />\\n </Thumbnail>\\n <VideoDetails id={video.id} />\\n </div>\\n </Layout>\\n );\\n}\\n\\nfunction VideoInfo({ id }) {\\n const details = use(fetchVideoDetails(id));\\n return (\\n <>\\n <p className=\\"info-title\\">{details.title}</p>\\n <p className=\\"info-description\\">{details.description}</p>\\n </>\\n );\\n}\\n\\n
You can also use <ViewTransition>
to animate lists of items as they re-order, like in a searchable list of items:
<div className=\\"videos\\">
{filteredVideos.map((video) => (
<ViewTransition key={video.id}>
<Video video={video} />
</ViewTransition>
))}
</div>
To activate the ViewTransition, we can use useDeferredValue
:
const [searchText, setSearchText] = useState(\'\');
const deferredSearchText = useDeferredValue(searchText);
const filteredVideos = filterVideos(videos, deferredSearchText);
Now the items animate as you type in the search bar:
\\nimport { useId, useState, use, useDeferredValue, unstable_ViewTransition as ViewTransition } from \\"react\\";import { Video } from \\"./Videos\\";import Layout from \\"./Layout\\";import { fetchVideos } from \\"./data\\";import { IconSearch } from \\"./Icons\\";\\n\\nfunction SearchList({searchText, videos}) {\\n // Activate with useDeferredValue (\\"when\\") \\n const deferredSearchText = useDeferredValue(searchText);\\n const filteredVideos = filterVideos(videos, deferredSearchText);\\n return (\\n <div className=\\"video-list\\">\\n <div className=\\"videos\\">\\n {filteredVideos.map((video) => (\\n // Animate each item in list (\\"what\\") \\n <ViewTransition key={video.id}>\\n <Video video={video} />\\n </ViewTransition>\\n ))}\\n </div>\\n {filteredVideos.length === 0 && (\\n <div className=\\"no-results\\">No results</div>\\n )}\\n </div>\\n );\\n}\\n\\nexport default function Home() {\\n const videos = use(fetchVideos());\\n const count = videos.length;\\n const [searchText, setSearchText] = useState(\'\');\\n \\n return (\\n <Layout heading={<div className=\\"fit\\">{count} Videos</div>}>\\n <SearchInput value={searchText} onChange={setSearchText} />\\n <SearchList videos={videos} searchText={searchText} />\\n </Layout>\\n );\\n}\\n\\nfunction SearchInput({ value, onChange }) {\\n const id = useId();\\n return (\\n <form className=\\"search\\" onSubmit={(e) => e.preventDefault()}>\\n <label htmlFor={id} className=\\"sr-only\\">\\n Search\\n </label>\\n <div className=\\"search-input\\">\\n <div className=\\"search-icon\\">\\n <IconSearch />\\n </div>\\n <input\\n type=\\"text\\"\\n id={id}\\n placeholder=\\"Search\\"\\n value={value}\\n onChange={(e) => onChange(e.target.value)}\\n />\\n </div>\\n </form>\\n );\\n}\\n\\nfunction filterVideos(videos, query) {\\n const keywords = query\\n .toLowerCase()\\n .split(\\" \\")\\n .filter((s) => s !== \\"\\");\\n if (keywords.length === 0) {\\n return videos;\\n }\\n return videos.filter((video) => {\\n const words = (video.title + \\" \\" + video.description)\\n .toLowerCase()\\n .split(\\" \\");\\n return keywords.every((kw) => words.some((w) => w.includes(kw)));\\n });\\n}\\n\\n
By adding a few <ViewTransition>
components and a few lines of CSS, we were able to add all the animations above into the final result.
We’re excited about View Transitions and think they will level up the apps you’re able to build. They’re ready to start trying today in the experimental channel of React releases.
\\nLet’s remove the slow fade, and take a look at the final result:
\\nimport {unstable_ViewTransition as ViewTransition} from \'react\'; import Details from \'./Details\'; import Home from \'./Home\'; import {useRouter} from \'./router\';\\n\\nexport default function App() {\\n const {url} = useRouter();\\n\\n // Animate with a cross fade between pages.\\n return (\\n <ViewTransition key={url}>\\n {url === \'/\' ? <Home /> : <Details />}\\n </ViewTransition>\\n );\\n}\\n\\n
If you’re curious to know more about how they work, check out How Does <ViewTransition>
Work in the docs.
For more background on how we built View Transitions, see: #31975, #32105, #32041, #32734, #32797 #31999, #32031, #32050, #32820, #32029, #32028, and #32038 by @sebmarkbage (thanks Seb!).
\\nIn past updates, we shared that we were researching an API to allow components to be visually hidden and deprioritized, preserving UI state with reduced performance costs relative to unmounting or hiding with CSS.
\\nWe’re now ready to share the API and how it works, so you can start testing it in experimental React versions.
\\n<Activity>
is a new component to hide and show parts of the UI:
<Activity mode={isVisible ? \'visible\' : \'hidden\'}>
<Page />
</Activity>
When an Activity is visible it’s rendered as normal. When an Activity is hidden it is unmounted, but will save its state and continue to render at a lower priority than anything visible on screen.
\\nYou can use Activity
to save state for parts of the UI the user isn’t using, or pre-render parts that a user is likely to use next.
Let’s look at some examples improving the View Transition examples above.
\\nEffects don’t mount when an Activity is hidden.
When an <Activity>
is hidden
, Effects are unmounted. Conceptually, the component is unmounted, but React saves the state for later.
In practice, this works as expected if you have followed the You Might Not Need an Effect guide. To eagerly find problematic Effects, we recommend adding <StrictMode>
which will eagerly perform Activity unmounts and mounts to catch any unexpected side effects.
When a user navigates away from a page, it’s common to stop rendering the old page:
\\nfunction App() {
const { url } = useRouter();
return (
<>
{url === \'/\' && <Home />}
{url !== \'/\' && <Details />}
</>
);
}
However, this means if the user goes back to the old page, all of the previous state is lost. For example, if the <Home />
page has an <input>
field, when the user leaves the page the <input
> is unmounted, and all of the text they had typed is lost.
Activity allows you to keep the state around as the user changes pages, so when they come back they can resume where they left off. This is done by wrapping part of the tree in <Activity>
and toggling the mode
:
function App() {
const { url } = useRouter();
return (
<>
<Activity mode={url === \'/\' ? \'visible\' : \'hidden\'}>
<Home />
</Activity>
{url !== \'/\' && <Details />}
</>
);
}
With this change, we can improve on our View Transitons example above. Before, when you searched for a video, selected one, and returned, your search filter was lost. With Activity, your search filter is restored and you can pick up where you left off.
\\nTry searching for a video, selecting it, and clicking “back”:
\\nimport { unstable_ViewTransition as ViewTransition, unstable_Activity as Activity } from \\"react\\"; import Details from \\"./Details\\"; import Home from \\"./Home\\"; import { useRouter } from \\"./router\\";\\n\\nexport default function App() {\\n const { url } = useRouter();\\n \\n return (\\n // View Transitions know about Activity\\n <ViewTransition>\\n {/* Render Home in Activity so we don\'t lose state */}\\n <Activity mode={url === \'/\' ? \'visible\' : \'hidden\'}>\\n <Home />\\n </Activity>\\n {url !== \'/\' && <Details />}\\n </ViewTransition>\\n );\\n}\\n\\n
Sometimes, you may want to prepare the next part of the UI a user is likely to use ahead of time, so it’s ready by the time they are ready to use it. This is especially useful if the next route needs to suspend on data it needs to render, because you can help ensure the data is already fetched before the user navigates.
\\nFor example, our app currently needs to suspend to load the data for each video when you select one. We can improve this by rendering all of the pages in a hidden <Activity>
until the user navigates:
<ViewTransition>
<Activity mode={url === \'/\' ? \'visible\' : \'hidden\'}>
<Home />
</Activity>
<Activity mode={url === \'/details/1\' ? \'visible\' : \'hidden\'}>
<Details id={id} />
</Activity>
<Activity mode={url === \'/details/1\' ? \'visible\' : \'hidden\'}>
<Details id={id} />
</Activity>
<ViewTransition>
With this update, if the content on the next page has time to pre-render, it will animate in without the Suspense fallback. Click a video, and notice that the video title and description on the Details page render immediately, without a fallback:
\\nimport { unstable_ViewTransition as ViewTransition, unstable_Activity as Activity, use } from \\"react\\"; import Details from \\"./Details\\"; import Home from \\"./Home\\"; import { useRouter } from \\"./router\\"; import {fetchVideos} from \'./data\'\\n\\nexport default function App() {\\n const { url } = useRouter();\\n const videoId = url.split(\\"/\\").pop();\\n const videos = use(fetchVideos());\\n \\n return (\\n <ViewTransition>\\n {/* Render videos in Activity to pre-render them */}\\n {videos.map(({id}) => (\\n <Activity key={id} mode={videoId === id ? \'visible\' : \'hidden\'}>\\n <Details id={id}/>\\n </Activity>\\n ))}\\n <Activity mode={url === \'/\' ? \'visible\' : \'hidden\'}>\\n <Home />\\n </Activity>\\n </ViewTransition>\\n );\\n}\\n\\n
When using Activity on a page that uses server-side rendering (SSR), there are additional optimizations.
\\nIf part of the page is rendered with mode=\\"hidden\\"
, then it will not be included in the SSR response. Instead, React will schedule a client render for the content inside Activity while the rest of the page hydrates, prioritizing the visible content on screen.
For parts of the UI rendered with mode=\\"visible\\"
, React will de-prioritize hydration of content within Activity, similar to how Suspense content is hydrated at a lower priority. If the user interacts with the page, we’ll prioritize hydration within the boundary if needed.
These are advanced use cases, but they show the additional benefits considered with Activity.
\\nIn the future, we may add more modes to Activity.
\\nFor example, a common use case is rendering a modal, where the previous “inactive” page is visible behind the “active” modal view. The “hidden” mode does not work for this use case because it’s not visible and not included in SSR.
\\nInstead, we’re considering a new mode that would keep the content visible—and included in SSR—but keep it unmounted and de-prioritize updates. This mode may also need to “pause” DOM updates, since it can be distracting to see backgrounded content updating while a modal is open.
\\nAnother mode we’re considering for Activity is the ability to automatically destroy state for hidden Activities if there is too much memory being used. Since the component is already unmounted, it may be preferable to destroy state for the least recently used hidden parts of the app rather than consume too many resources.
\\nThese are areas we’re still exploring, and we’ll share more as we make progress. For more information on what Activity includes today, check out the docs.
\\nWe’re also developing features to help solve the common problems below.
\\nAs we iterate on possible solutions, you may see some potenial APIs we’re testing being shared based on the PRs we are landing. Please keep in mind, that as we try different ideas, we often change or remove different solutions after trying them out.
\\nWhen the solutions we’re working on are shared too early, it can create churn and confusion in the community. To balance being transparent and limiting confusion, we’re sharing the problems we’re currently developing solutions for, without sharing a particular solution we have in mind.
\\nAs these features progress, we’ll announce them on the blog with docs included so you can try them out.
\\nWe’re working on a new set of custom tracks to performance profilers using browser APIs that allow adding custom tracks to provide more information about the performance of your React app.
\\nThis feature is still in progress, so we’re not ready to publish docs to fully release it as an experimental feature yet. You can get a sneak preview when using an experimental version of React, which will automatically see the performance tracks added to profiles:
\\nThere are a few known issues we plan to address such as performance, and the scheduler track not always “connecting” work across Suspended trees, so it’s not quite ready to try. We’re also still collecting feedback from early adopters to improve the design and usability of the tracks.
\\nOnce we solve those issues, we’ll publish experimental docs and share that it’s ready to try.
\\nWhen we released hooks, we had three motivations:
\\nSince their release, hooks have been successful at sharing code between components. Hooks are now the favored way to share logic between components, and there are less use cases for render props and higher order components. Hooks have also been successful at supporting features like Fast Refresh that were not possible with class components.
\\nUnfortunately, some hooks are still hard to think in terms of function instead of lifecycles. Effects specifically are still hard to understand and is the most common pain point we hear from developers. Last year, we spent a significant amount of time researching how Effects were used, and how those use cases could be simplified and easier to understand.
\\nWe found that often, the confusion is from using an Effect when you don’t need to. The You Might Not Need an Effect guide, covers many cases for when Effects are not the right solution. However, even when an Effect is the right fit for a problem, Effects can still be harder to understand than class component lifecyles.
\\nWe believe one of the reasons for confusion is that developers to think of Effects from the components perspective (like a lifecycle), instead of the Effects point of view (what the Effect does).
\\nLet’s look at an example from the docs:
\\nuseEffect(() => {
// Your Effect connected to the room specified with roomId...
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
// ...until it disconnected
connection.disconnect();
};
}, [roomId]);
Many users would read this code as “on mount, connect to the roomId. whenever roomId
changes, disconnect to the old room and re-create the connection”. However, this is thinking from the component’s lifecycle perspective, which means you will need to think of every component lifecycle state to write the Effect correctly. This can be difficult, so it’s understandble that Effects seem harder than class lifecycles when using component perspective.
Instead, it’s better to think from the Effect’s perspective. The Effect doesn’t know about the component lifecycles. It only describes how to start synchronization and how to stop it. When users think of Effects in this way, their Effects tend to be easier to write, and more resilient to being started and stopped as many times as it’s needed.
\\nWe spent some time researching why Effects are thought of from the component perspective, and we think one of the resons is the dependency array. Since you have to write it, it’s right there and in your face reminding you of what you’re “reacting” to and baiting you into the mental model of ‘do this when these values change’.
\\nWhen we released hooks, we knew we could make them easier to use with ahead-of-time compilation. With the React Compiler, you’re now able to avoid writing useCallback
and useMemo
yourself in most cases. For Effects, the compiler can insert the dependencies for you:
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}); // compiler inserted dependencies.
With this code, the React Compiler can infer the dependencies for you and insert them automatically so you don’t need to see or write them. With features like the IDE exension and useEffectEvent
, we can provide a CodeLens to show you what the Compiler inserted for times you need to debug, or to optimize by removing a dependency. This helps reinforce the correct mental model for writing Effects, which can run at any time to synchronize your component or hook’s state with something else.
Our hope is that automatically inserting dependencies is not only easier to write, but that it also makes them easier to understand by forcing you to think in terms of what the Effect does, and not in component lifecycles.
\\nEarlier this week we shared the React Compiler release candidate, and we’re working towards shipping the first SemVer stable version of the compiler in the coming months.
\\nWe’ve also begun exploring ways to use the React Compiler to provide information that can improve understanding and debugging your code. One idea we’ve started exploring is a new experimental LSP-based React IDE extension powered by React Compiler, similar to the extension used in Lauren Tan’s React Conf talk.
\\nOur idea is that we can use the compiler’s static analysis to provide more information, suggestions, and optimization opportunities directly in your IDE. For example, we can provide diagnostics for code breaking the Rules of React, hovers to show if components and hooks were optimized by the compiler, or a CodeLens to see automatically inserted Effect dependencies.
\\nThe IDE extension is still an early exploration, but we’ll share our progress in future updates.
\\nMany DOM APIs like those for event management, positioning, and focus are difficult to compose when writing with React. This often leads developers to reach for Effects, managing multiple Refs, by using APIs like findDOMNode
(removed in React 19).
We are exploring adding refs to Fragments that would point to a group of DOM elements, rather than just a single element. Our hope is that this will simplify managing multiple children and make it easier to write composable React code when calling DOM APIs.
\\nFragment refs are still being researched. We’ll share more when we’re closer to having the final API finished.
\\nWe’re also researching ways to enhance View Transitions to support gesture animations such as swiping to open a menu, or scroll through a photo carousel.
\\nGestures present new challenges for a few reasons:
\\nWe believe we’ve found an approach that works well and may introduce a new API for triggering gesture transitions. For now, we’re focused on shipping <ViewTransition>
, and will revisit gestures afterward.
When we released React 18 with concurrent rendering, we also released useSyncExternalStore
so external store libraries that did not use React state or context could support concurrent rendering by forcing a sync render when the store is updated.
Using useSyncExternalStore
comes at a cost though, since it forces bail out from concurrent features like transitions, and forces existing content to show Suspense fallbacks.
Now that React 19 has shipped, we’re re-visiting this problem space to create a primitive to fully support concurrent external stores with the use
API:
const value = use(store);
Our goal is to allow external state to be read during render without tearing, and to work seamlessly with all of the concurrent features React offers.
\\nThis research is still early. We’ll share more, and what the new APIs will look like, when we’re further along.
\\nThanks to Aurora Scharff, Dan Abramov, Eli White, Lauren Tan, Luna Wei, Matt Carroll, Jack Pope, Jason Bonta, Jordan Brown, Jordan Eldredge, Mofei Zhang, Sebastien Lorber, Sebastian Markbåge, and Tim Yung for reviewing this post.
April 21, 2025 by Lauren Tan and Mofei Zhang.
\\nThe React team is excited to share new updates:
eslint-plugin-react-compiler
into eslint-plugin-react-hooks
.React Compiler is a build-time tool that optimizes your React app through automatic memoization. Last year, we published React Compiler’s first beta and received lots of great feedback and contributions. We’re excited about the wins we’ve seen from folks adopting the compiler (see case studies from Sanity Studio and Wakelet) and are working towards a stable release.
\\nWe are releasing the compiler’s first Release Candidate (RC) today. The RC is intended to be a stable and near-final version of the compiler, and safe to use in production for all React users.
\\nTo install the RC:
\\nnpm
\\npnpm
\\nyarn
\\nAs part of the RC, we’ve been making React Compiler easier to add to your projects and added optimizations to how the compiler generates memoization. React Complier now supports optional chains and array indices as dependencies. We’re exploring how to infer even more dependencies like equality checks and string interpolation. These improvements ultimately result in fewer re-renders and more responsive UIs.
\\nWe have also heard from the community that the ref-in-render validation sometimes has false positives. Since as a general philosophy we want you to be able to fully trust in the compiler’s error messages and hints, we are turning it off by default for now. We will keep working to improve this validation, and we will re-enable it in a follow up release.
\\nYou can find more details on using the Compiler in our docs.
\\nAs noted in the Beta announcement, React Compiler is compatible with React 17 and up. If you are not yet on React 19, you can use React Compiler by specifying a minimum target in your compiler config, and adding react-compiler-runtime
as a dependency. You can find docs on this here.
If you have already installed eslint-plugin-react-compiler, you can now remove it and use eslint-plugin-react-hooks@^6.0.0-rc.1
. Many thanks to @michaelfaith for contributing to this improvement!
To install:
\\nnpm
\\npnpm
\\nyarn
\\n// eslint.config.js
import * as reactHooks from \'eslint-plugin-react-hooks\';
export default [
// Flat Config (eslint 9+)
reactHooks.configs.recommended,
// Legacy Config
reactHooks.configs[\'recommended-latest\']
];
The linter does not require the compiler to be installed, so there’s no risk in upgrading eslint-plugin-react-hooks. We recommend everyone upgrade today.
\\nReact Compiler can be installed across several build tools such as Babel, Vite, and Rsbuild.
\\nIn addition to those tools, we have been collaborating with Kang Dongyoong (@kdy1dev) from the swc team on adding additional support for React Compiler as an swc plugin. As part of the RC release, you can now integrate the compiler into your Next.js app with swc instead of Babel.
\\nNext.js users can upgrade to 15.3.1 or greater to try this out. If you have already enabled the compiler in your Next.js’s config, swc support will be enabled automatically.
\\nVite users can continue to use vite-plugin-react to enable the compiler, by adding it as a Babel plugin. We are also working with the oxc team to add support for the compiler. Once rolldown is officially released and supported in Vite and oxc support is added for React Compiler, we’ll update the docs with information on how to migrate.
\\nReact Compiler works best when the auto-memoization applied is strictly for performance. Future versions of the compiler may change how memoization is applied, for example it could become more granular and precise.
\\nHowever, because product code may sometimes break the rules of React in ways that aren’t always statically detectable in JavaScript, changing memoization can occasionally have unexpected results. For example, a previously memoized value might be used as a dependency for a useEffect somewhere in the component tree. Changing how or whether this value is memoized can cause over or under-firing of that useEffect. While we encourage useEffect only for synchronization, your codebase may have useEffects that cover other use-cases. For example you may have an effect that runs in response to some value changing (breaks the rules of React).
\\nIn other words, changing memoization may under rare circumstances cause unexpected behavior. For this reason, we recommend following the Rules of React and employing continuous end-to-end testing of your app so you can upgrade the compiler with confidence and identify any rules of React violations that might cause issues.
\\nIf you don’t have good test coverage, we recommend pinning the compiler to an exact version (eg 19.0.0
) rather than a SemVer range (eg ^19.0.0
). You can do this by passing the --save-exact
(npm/pnpm) or --exact
flags (yarn) when upgrading the compiler. You should then do any upgrades of the compiler manually, taking care to check that your app still works as expected.
This is not a final roadmap, and is subject to change.
\\nAfter a period of final feedback from the community on the RC, we plan on a Stable Release for the compiler.
\\nPost-Stable, we plan to add more compiler optimizations and improvements. This includes both continual improvements to automatic memoization, and new optimizations altogether, with minimal to no change of product code. Each upgrade will continue to improve performance and add better handling of diverse JavaScript and React patterns.
\\nDuring the RC period, we encourage all React users to try the compiler and provide feedback in the React repo. Please open an issue if you encounter any bugs or unexpected behavior. If you have a general question or suggestion, please post them in the React Compiler Working Group.
\\nThanks to Joe Savona, Jason Bonta, Jimmy Lai, and Kang Dongyoon (@kdy1dev) for reviewing and editing this post.
February 14, 2025 by Matt Carroll and Ricky Hanlon
\\nToday, we’re deprecating Create React App for new apps, and encouraging existing apps to migrate to a framework. We’re also providing docs for when a framework isn’t a good fit for your project, or you prefer to start by building a framework.
When we released Create React App in 2016, there was no clear way to build a new React app.
\\nTo create a React app, you had to install a bunch of tools and wire them up together yourself to support basic features like JSX, linting, and hot reloading. This was very tricky to do correctly, so the community created boilerplates for common setups. However, boilerplates were difficult to update and fragmentation made it difficult for React to release new features.
\\nCreate React App solved these problems by combining several tools into a single recommended configuration. This allowed apps a simple way to upgrade to new tooling features, and allowed the React team to deploy non-trivial tooling changes (Fast Refresh support, React Hooks lint rules) to the broadest possible audience.
\\nThis model became so popular that there’s an entire category of tools working this way today.
\\nAlthough Create React App makes it easy to get started, there are several limitations that make it difficult to build high performant production apps. In principle, we could solve these problems by essentially evolving it into a framework.
\\nHowever, since Create React App currently has no active maintainers, and there are many existing frameworks that solve these problems already, we’ve decided to deprecate Create React App.
\\nStarting today, if you install a new app, you will see a deprecation warning:
\\nWe recommend creating new React apps with a framework. All the frameworks we recommend support client-only SPAs, and can be deployed to a CDN or static hosting service without a server.
\\nFor existing apps, these guides will help you migrate to a client-only SPA:
\\nCreate React App will continue working in maintenance mode, and we’ve published a new version of Create React App to work with React 19.
\\nIf your app has unusual constraints, or you prefer to solve these problems by building your own framework, or you just want to learn how react works from scratch, you can roll your own custom setup with React using Vite or Parcel.
\\nTo help users get started with Vite or Parcel, we’ve published new docs for Building a Framework. Continue reading to learn more about the limitations of Create React App and why we recommend frameworks.
\\nWe provide several Vite-based recommendations.
React Router v7 is a Vite based framework which allows you to use Vite’s fast development server and build tooling with a framework that provides routing and data fetching. Just like the other frameworks we recommend, you can build a SPA with React Router v7.
We also recommend using Vite when adding React to an existing project, or building a framework.
Just like Svelte has Sveltekit, Vue has Nuxt, and Solid has SolidStart, React recommends using a framework that integrates with build tools like Vite for new projects.
Create React App and build tools like it make it easy to get started building a React app. After running npx create-react-app my-app
, you get a fully configured React app with a development server, linting, and a production build.
For example, if you’re building an internal admin tool, you can start with a landing page:
\\nexport default function App() {
return (
<div>
<h1>Welcome to the Admin Tool!</h1>
</div>
)
}
This allows you to immediately start coding in React with features like JSX, default linting rules, and a bundler to run in both development and production. However, this setup is missing the tools you need to build a real production app.
\\nMost production apps need solutions to problems like routing, data fetching, and code splitting.
\\nCreate React App does not include a specific routing solution. If you’re just getting started, one option is to use useState
to switch between routes. But doing this means that you can’t share links to your app - every link would go to the same page - and structuring your app becomes difficult over time:
import {useState} from \'react\';
import Home from \'./Home\';
import Dashboard from \'./Dashboard\';
export default function App() {
// ❌ Routing in state does not create URLs
const [route, setRoute] = useState(\'home\');
return (
<div>
{route === \'home\' && <Home />}
{route === \'dashboard\' && <Dashbord />}
</div>
)
}
This is why most apps that use Create React App solve add routing with a routing library like React Router or Tanstack Router. With a routing library, you can add additional routes to the app, which provides opinions on the structure of your app, and allows you to start sharing links to routes. For example, with React Router you can define routes:
\\nimport {RouterProvider, createBrowserRouter} from \'react-router\';
import Home from \'./Home\';
import Dashboard from \'./Dashboard\';
// ✅ Each route has it\'s own URL
const router = createBrowserRouter([
{path: \'/\', element: <Home />},
{path: \'/dashboard\', element: <Dashboard />}
]);
export default function App() {
return (
<RouterProvider value={router} />
)
}
With this change, you can share a link to /dashboard
and the app will navigate to the dashboard page . Once you have a routing library, you can add additional features like nested routes, route guards, and route transitions, which are difficult to implement without a routing library.
There’s a tradeoff being made here: the routing library adds complexity to the app, but it also adds features that are difficult to implement without it.
\\nAnother common problem in Create React App is data fetching. Create React App does not include a specific data fetching solution. If you’re just getting started, a common option is to use fetch
in an effect to load data.
But doing this means that the data is fetched after the component renders, which can cause network waterfalls. Network waterfalls are caused by fetching data when your app renders instead of in parallel while the code is downloading:
\\nexport default function Dashboard() {
const [data, setData] = useState(null);
// ❌ Fetching data in a component causes network waterfalls
useEffect(() => {
fetch(\'/api/data\')
.then(response => response.json())
.then(data => setData(data));
}, []);
return (
<div>
{data.map(item => <div key={item.id}>{item.name}</div>)}
</div>
)
}
Fetching in an effect means the user has to wait longer to see the content, even though the data could have been fetched earlier. To solve this, you can use a data fetching library like React Query, SWR, Apollo, or Relay which provide options to prefetch data so the request is started before the component renders.
\\nThese libraries work best when integrated with your routing “loader” pattern to specify data dependencies at the route level, which allows the router to optimize your data fetches:
\\nexport async function loader() {
const response = await fetch(`/api/data`);
const data = await response.json();
return data;
}
// ✅ Fetching data in parallel while the code is downloading
export default function Dashboard({loaderData}) {
return (
<div>
{loaderData.map(item => <div key={item.id}>{item.name}</div>)}
</div>
)
}
On initial load, the router can fetch the data immediately before the route is rendered. As the user navigates around the app, the router is able to fetch both the data and the route at the same time, parallelizing the fetches. This reduces the time it takes to see the content on the screen, and can improve the user experience.
\\nHowever, this requires correctly configuring the loaders in your app and trades off complexity for performance.
\\nAnother common problem in Create React App is code splitting. Create React App does not include a specific code splitting solution. If you’re just getting started, you might not consider code splitting at all.
\\nThis means your app is shipped as a single bundle:
\\n- bundle.js 75kb
But for ideal performance, you should “split” your code into separate bundles so the user only needs to download what they need. This decreases the time the user needs to wait to load your app, by only downloading the code they need to see the page they are on.
\\n- core.js 25kb
- home.js 25kb
- dashboard.js 25kb
One way to do code-splitting is with React.lazy
. However, this means that the code is not fetched until the component renders, which can cause network waterfalls. A more optimal solution is to use a router feature that fetches the code in parallel while the code is downloading. For example, React Router provides a lazy
option to specify that a route should be code split and optimize when it is loaded:
import Home from \'./Home\';
import Dashboard from \'./Dashboard\';
// ✅ Routes are downloaded before rendering
const router = createBrowserRouter([
{path: \'/\', lazy: () => import(\'./Home\')},
{path: \'/dashboard\', lazy: () => import(\'Dashboard\')}
]);
Optimized code-splitting is tricky to get right, and it’s easy to make mistakes that can cause the user to download more code than they need. It works best when integrated with your router and data loading solutions to maximize caching, parallelize fetches, and support “import on interaction” patterns.
\\nThese are just a few examples of the limitations of Create React App.
\\nOnce you’ve integrated routing, data-fetching, and code splitting, you now also need to consider pending states, navigation interruptions, error messages to the user, and revalidation of the data. There are entire categories of problems that users need to solve like:
\\nAll of these work together to create the most optimal loading sequence.
\\nSolving each of these problems individually in Create React App can be difficult as each problem is interconnected with the others and can require deep expertise in problem areas users may not be familiar with. In order to solve these problems, users end up building their own bespoke solutions on top of Create React App, which was the problem Create React App originally tried to solve.
\\nAlthough you could solve all these pieces yourself in a build tool like Create React App, Vite, or Parcel, it is hard to do well. Just like when Create React App itself integrated several build tools together, you need a tool to integrate all of these features together to provide the best experience to users.
\\nThis category of tools that integrates build tools, rendering, routing, data fetching, and code splitting are known as “frameworks” — or if you prefer to call React itself a framework, you might call them “metaframeworks”.
\\nFrameworks impose some opinions about structuring your app in order to provide a much better user experience, in the same way build tools impose some opinions to make tooling easier. This is why we started recommending frameworks like Next.js, React Router, and Expo for new projects.
\\nFrameworks provide the same getting started experience as Create React App, but also provide solutions to problems users need to solve anyway in real production apps.
\\nThe frameworks we recommend all provide the option to create a client-side rendered (CSR) app.
In some cases, CSR is the right choice for a page, but many times it’s not. Even if most of your app is client-side, there are often individual pages that could benefit from server rendering features like static-site generation (SSG) or server-side rendering (SSR), for example a Terms of Service page, or documentation.
Server rendering generally sends less JavaScript to the client, and a full HTML document which produces a faster First Contentful Paint (FCP) by reducing Total Blocking Time (TBD), which can also lower Interaction to Next Paint (INP). This is why the Chrome team has encouraged developers to consider static or server-side render over a full client-side approach to achieve the best possible performance.
There are tradeoffs to using a server, and it is not always the best option for every page. Generating pages on the server incurs additional cost and takes time to generate which can increase Time to First Byte (TTFB). The best performing apps are able to pick the right rendering strategy on a per-page basis, based on the tradeoffs of each strategy.
Frameworks provide the option to use a server on any page if you want to, but do not force you to use a server. This allows you to pick the right rendering strategy for each page in your app.
The frameworks we recommend also include support for React Server Components.
Server Components help solve these problems by moving routing and data fetching to the server, and allowing code splitting to be done for client components based on the data you render, instead of just the route rendered, and reducing the amount of JavaScript shipped for the best possible loading sequence.
Server Components do not require a server. They can be run at build time on your CI server to create a static-site generated app (SSG) app, at runtime on a web server for a server-side rendered (SSR) app.
See Introducing zero-bundle size React Server Components and the docs for more info.
A common misunderstanding is that server rendering is only for SEO.
While server rendering can improve SEO, it also improves performance by reducing the amount of JavaScript the user needs to download and parse before they can see the content on the screen.
This is why the Chrome team has encouraged developers to consider static or server-side render over a full client-side approach to achieve the best possible performance.
Thank you to Dan Abramov for creating Create React App, and Joe Haddad, Ian Schmitz, Brody McKee, and many others for maintaining Create React App over the years. Thank you to Brooks Lybrand, Dan Abramov, Devon Govett, Eli White, Jack Herrington, Joe Savona, Lauren Tan, Lee Robinson, Mark Erikson, Ryan Florance, Sophie Alpert, Tanner Linsley, and Theo Browne for reviewing and providing feedback on this post.
December 05, 2024 by The React Team
\\nAdditions since this post was originally shared with the React 19 RC in April:
The date for this post has been update to reflect the stable release date.
React v19 is now available on npm!
In our React 19 Upgrade Guide, we shared step-by-step instructions for upgrading your app to React 19. In this post, we’ll give an overview of the new features in React 19, and how you can adopt them.
\\n\\nFor a list of breaking changes, see the Upgrade Guide.
\\nA common use case in React apps is to perform a data mutation and then update state in response. For example, when a user submits a form to change their name, you will make an API request, and then handle the response. In the past, you would need to handle pending states, errors, optimistic updates, and sequential requests manually.
\\nFor example, you could handle the pending and error state in useState
:
// Before Actions
function UpdateName({}) {
const [name, setName] = useState(\\"\\");
const [error, setError] = useState(null);
const [isPending, setIsPending] = useState(false);
const handleSubmit = async () => {
setIsPending(true);
const error = await updateName(name);
setIsPending(false);
if (error) {
setError(error);
return;
}
redirect(\\"/path\\");
};
return (
<div>
<input value={name} onChange={(event) => setName(event.target.value)} />
<button onClick={handleSubmit} disabled={isPending}>
Update
</button>
{error && <p>{error}</p>}
</div>
);
}
In React 19, we’re adding support for using async functions in transitions to handle pending states, errors, forms, and optimistic updates automatically.
\\nFor example, you can use useTransition
to handle the pending state for you:
// Using pending state from Actions
function UpdateName({}) {
const [name, setName] = useState(\\"\\");
const [error, setError] = useState(null);
const [isPending, startTransition] = useTransition();
const handleSubmit = () => {
startTransition(async () => {
const error = await updateName(name);
if (error) {
setError(error);
return;
}
redirect(\\"/path\\");
})
};
return (
<div>
<input value={name} onChange={(event) => setName(event.target.value)} />
<button onClick={handleSubmit} disabled={isPending}>
Update
</button>
{error && <p>{error}</p>}
</div>
);
}
The async transition will immediately set the isPending
state to true, make the async request(s), and switch isPending
to false after any transitions. This allows you to keep the current UI responsive and interactive while the data is changing.
Actions automatically manage submitting data for you:
useOptimistic
hook so you can show users instant feedback while the requests are submitting.<form>
elements now support passing functions to the action
and formAction
props. Passing functions to the action
props use Actions by default and reset the form automatically after submission.Building on top of Actions, React 19 introduces useOptimistic
to manage optimistic updates, and a new hook React.useActionState
to handle common cases for Actions. In react-dom
we’re adding <form>
Actions to manage forms automatically and useFormStatus
to support the common cases for Actions in forms.
In React 19, the above example can be simplified to:
\\n// Using <form> Actions and useActionState
function ChangeName({ name, setName }) {
const [error, submitAction, isPending] = useActionState(
async (previousState, formData) => {
const error = await updateName(formData.get(\\"name\\"));
if (error) {
return error;
}
redirect(\\"/path\\");
return null;
},
null,
);
return (
<form action={submitAction}>
<input type=\\"text\\" name=\\"name\\" />
<button type=\\"submit\\" disabled={isPending}>Update</button>
{error && <p>{error}</p>}
</form>
);
}
In the next section, we’ll break down each of the new Action features in React 19.
\\nuseActionState
To make the common cases easier for Actions, we’ve added a new hook called useActionState
:
const [error, submitAction, isPending] = useActionState(
async (previousState, newName) => {
const error = await updateName(newName);
if (error) {
// You can return any result of the action.
// Here, we return only the error.
return error;
}
// handle success
return null;
},
null,
);
useActionState
accepts a function (the “Action”), and returns a wrapped Action to call. This works because Actions compose. When the wrapped Action is called, useActionState
will return the last result of the Action as data
, and the pending state of the Action as pending
.
React.useActionState
was previously called ReactDOM.useFormState
in the Canary releases, but we’ve renamed it and deprecated useFormState
.
See #28491 for more info.
For more information, see the docs for useActionState
.
<form>
Actions Actions are also integrated with React 19’s new <form>
features for react-dom
. We’ve added support for passing functions as the action
and formAction
props of <form>
, <input>
, and <button>
elements to automatically submit forms with Actions:
<form action={actionFunction}>
When a <form>
Action succeeds, React will automatically reset the form for uncontrolled components. If you need to reset the <form>
manually, you can call the new requestFormReset
React DOM API.
For more information, see the react-dom
docs for <form>
, <input>
, and <button>
.
useFormStatus
In design systems, it’s common to write design components that need access to information about the <form>
they’re in, without drilling props down to the component. This can be done via Context, but to make the common case easier, we’ve added a new hook useFormStatus
:
import {useFormStatus} from \'react-dom\';
function DesignButton() {
const {pending} = useFormStatus();
return <button type=\\"submit\\" disabled={pending} />
}
useFormStatus
reads the status of the parent <form>
as if the form was a Context provider.
For more information, see the react-dom
docs for useFormStatus
.
useOptimistic
Another common UI pattern when performing a data mutation is to show the final state optimistically while the async request is underway. In React 19, we’re adding a new hook called useOptimistic
to make this easier:
function ChangeName({currentName, onUpdateName}) {
const [optimisticName, setOptimisticName] = useOptimistic(currentName);
const submitAction = async formData => {
const newName = formData.get(\\"name\\");
setOptimisticName(newName);
const updatedName = await updateName(newName);
onUpdateName(updatedName);
};
return (
<form action={submitAction}>
<p>Your name is: {optimisticName}</p>
<p>
<label>Change Name:</label>
<input
type=\\"text\\"
name=\\"name\\"
disabled={currentName !== optimisticName}
/>
</p>
</form>
);
}
The useOptimistic
hook will immediately render the optimisticName
while the updateName
request is in progress. When the update finishes or errors, React will automatically switch back to the currentName
value.
For more information, see the docs for useOptimistic
.
use
In React 19 we’re introducing a new API to read resources in render: use
.
For example, you can read a promise with use
, and React will Suspend until the promise resolves:
import {use} from \'react\';
function Comments({commentsPromise}) {
// `use` will suspend until the promise resolves.
const comments = use(commentsPromise);
return comments.map(comment => <p key={comment.id}>{comment}</p>);
}
function Page({commentsPromise}) {
// When `use` suspends in Comments,
// this Suspense boundary will be shown.
return (
<Suspense fallback={<div>Loading...</div>}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
)
}
use
does not support promises created in render. If you try to pass a promise created in render to use
, React will warn:
To fix, you need to pass a promise from a suspense powered library or framework that supports caching for promises. In the future we plan to ship features to make it easier to cache promises in render.
You can also read context with use
, allowing you to read Context conditionally such as after early returns:
import {use} from \'react\';
import ThemeContext from \'./ThemeContext\'
function Heading({children}) {
if (children == null) {
return null;
}
// This would not work with useContext
// because of the early return.
const theme = use(ThemeContext);
return (
<h1 style={{color: theme.color}}>
{children}
</h1>
);
}
The use
API can only be called in render, similar to hooks. Unlike hooks, use
can be called conditionally. In the future we plan to support more ways to consume resources in render with use
.
For more information, see the docs for use
.
We’ve added two new APIs to react-dom/static
for static site generation:
prerender
prerenderToNodeStream
These new APIs improve on renderToString
by waiting for data to load for static HTML generation. They are designed to work with streaming environments like Node.js Streams and Web Streams. For example, in a Web Stream environment, you can prerender a React tree to static HTML with prerender
:
import { prerender } from \'react-dom/static\';
async function handler(request) {
const {prelude} = await prerender(<App />, {
bootstrapScripts: [\'/main.js\']
});
return new Response(prelude, {
headers: { \'content-type\': \'text/html\' },
});
}
Prerender APIs will wait for all data to load before returning the static HTML stream. Streams can be converted to strings, or sent with a streaming response. They do not support streaming content as it loads, which is supported by the existing React DOM server rendering APIs.
\\nFor more information, see React DOM Static APIs.
\\nServer Components are a new option that allows rendering components ahead of time, before bundling, in an environment separate from your client application or SSR server. This separate environment is the “server” in React Server Components. Server Components can run once at build time on your CI server, or they can be run for each request using a web server.
\\nReact 19 includes all of the React Server Components features included from the Canary channel. This means libraries that ship with Server Components can now target React 19 as a peer dependency with a react-server
export condition for use in frameworks that support the Full-stack React Architecture.
While React Server Components in React 19 are stable and will not break between major versions, the underlying APIs used to implement a React Server Components bundler or framework do not follow semver and may break between minors in React 19.x.
To support React Server Components as a bundler or framework, we recommend pinning to a specific React version, or using the Canary release. We will continue working with bundlers and frameworks to stabilize the APIs used to implement React Server Components in the future.
For more, see the docs for React Server Components.
\\nServer Actions allow Client Components to call async functions executed on the server.
\\nWhen a Server Action is defined with the \\"use server\\"
directive, your framework will automatically create a reference to the server function, and pass that reference to the Client Component. When that function is called on the client, React will send a request to the server to execute the function, and return the result.
A common misunderstanding is that Server Components are denoted by \\"use server\\"
, but there is no directive for Server Components. The \\"use server\\"
directive is used for Server Actions.
For more info, see the docs for Directives.
Server Actions can be created in Server Components and passed as props to Client Components, or they can be imported and used in Client Components.
\\nFor more, see the docs for React Server Actions.
\\nref
as a prop Starting in React 19, you can now access ref
as a prop for function components:
function MyInput({placeholder, ref}) {
return <input placeholder={placeholder} ref={ref} />
}
//...
<MyInput ref={ref} />
New function components will no longer need forwardRef
, and we will be publishing a codemod to automatically update your components to use the new ref
prop. In future versions we will deprecate and remove forwardRef
.
refs
passed to classes are not passed as props since they reference the component instance.
We also improved error reporting for hydration errors in react-dom
. For example, instead of logging multiple errors in DEV without any information about the mismatch:
We now log a single message with a diff of the mismatch:
\\nif (typeof window !== \'undefined\')
.\\n- Variable input such as Date.now()
or Math.random()
which changes each time it’s called.\\n- Date formatting in a user’s locale which doesn’t match the server.\\n- External changing data without sending a snapshot of it along with the HTML.\\n- Invalid HTML tag nesting.\\n\\nIt can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\\n\\nhttps://react.dev/link/hydration-mismatch \\n\\n <App>\\n <span>\\n+ Client\\n- Server\\n\\n at throwOnHydrationMismatch\\n …<Context>
as a provider In React 19, you can render <Context>
as a provider instead of <Context.Provider>
:
const ThemeContext = createContext(\'\');
function App({children}) {
return (
<ThemeContext value=\\"dark\\">
{children}
</ThemeContext>
);
}
New Context providers can use <Context>
and we will be publishing a codemod to convert existing providers. In future versions we will deprecate <Context.Provider>
.
We now support returning a cleanup function from ref
callbacks:
<input
ref={(ref) => {
// ref created
// NEW: return a cleanup function to reset
// the ref when element is removed from DOM.
return () => {
// ref cleanup
};
}}
/>
When the component unmounts, React will call the cleanup function returned from the ref
callback. This works for DOM refs, refs to class components, and useImperativeHandle
.
Previously, React would call ref
functions with null
when unmounting the component. If your ref
returns a cleanup function, React will now skip this step.
In future versions, we will deprecate calling refs with null
when unmounting components.
Due to the introduction of ref cleanup functions, returning anything else from a ref
callback will now be rejected by TypeScript. The fix is usually to stop using implicit returns, for example:
- <div ref={current => (instance = current)} />
+ <div ref={current => {instance = current}} />
The original code returned the instance of the HTMLDivElement
and TypeScript wouldn’t know if this was supposed to be a cleanup function or if you didn’t want to return a cleanup function.
You can codemod this pattern with no-implicit-ref-callback-return
.
useDeferredValue
initial value We’ve added an initialValue
option to useDeferredValue
:
function Search({deferredValue}) {
// On initial render the value is \'\'.
// Then a re-render is scheduled with the deferredValue.
const value = useDeferredValue(deferredValue, \'\');
return (
<Results query={value} />
);
}
When initialValue is provided, useDeferredValue
will return it as value
for the initial render of the component, and schedules a re-render in the background with the deferredValue returned.
For more, see useDeferredValue
.
In HTML, document metadata tags like <title>
, <link>
, and <meta>
are reserved for placement in the <head>
section of the document. In React, the component that decides what metadata is appropriate for the app may be very far from the place where you render the <head>
or React does not render the <head>
at all. In the past, these elements would need to be inserted manually in an effect, or by libraries like react-helmet
, and required careful handling when server rendering a React application.
In React 19, we’re adding support for rendering document metadata tags in components natively:
\\nfunction BlogPost({post}) {
return (
<article>
<h1>{post.title}</h1>
<title>{post.title}</title>
<meta name=\\"author\\" content=\\"Josh\\" />
<link rel=\\"author\\" href=\\"https://twitter.com/joshcstory/\\" />
<meta name=\\"keywords\\" content={post.keywords} />
<p>
Eee equals em-see-squared...
</p>
</article>
);
}
When React renders this component, it will see the <title>
<link>
and <meta>
tags, and automatically hoist them to the <head>
section of document. By supporting these metadata tags natively, we’re able to ensure they work with client-only apps, streaming SSR, and Server Components.
For simple use cases, rendering Document Metadata as tags may be suitable, but libraries can offer more powerful features like overriding generic metadata with specific metadata based on the current route. These features make it easier for frameworks and libraries like react-helmet
to support metadata tags, rather than replace them.
For more info, see the docs for <title>
, <link>
, and <meta>
.
Stylesheets, both externally linked (<link rel=\\"stylesheet\\" href=\\"...\\">
) and inline (<style>...</style>
), require careful positioning in the DOM due to style precedence rules. Building a stylesheet capability that allows for composability within components is hard, so users often end up either loading all of their styles far from the components that may depend on them, or they use a style library which encapsulates this complexity.
In React 19, we’re addressing this complexity and providing even deeper integration into Concurrent Rendering on the Client and Streaming Rendering on the Server with built in support for stylesheets. If you tell React the precedence
of your stylesheet it will manage the insertion order of the stylesheet in the DOM and ensure that the stylesheet (if external) is loaded before revealing content that depends on those style rules.
function ComponentOne() {
return (
<Suspense fallback=\\"loading...\\">
<link rel=\\"stylesheet\\" href=\\"foo\\" precedence=\\"default\\" />
<link rel=\\"stylesheet\\" href=\\"bar\\" precedence=\\"high\\" />
<article class=\\"foo-class bar-class\\">
{...}
</article>
</Suspense>
)
}
function ComponentTwo() {
return (
<div>
<p>{...}</p>
<link rel=\\"stylesheet\\" href=\\"baz\\" precedence=\\"default\\" /> <-- will be inserted between foo & bar
</div>
)
}
During Server Side Rendering React will include the stylesheet in the <head>
, which ensures that the browser will not paint until it has loaded. If the stylesheet is discovered late after we’ve already started streaming, React will ensure that the stylesheet is inserted into the <head>
on the client before revealing the content of a Suspense boundary that depends on that stylesheet.
During Client Side Rendering React will wait for newly rendered stylesheets to load before committing the render. If you render this component from multiple places within your application React will only include the stylesheet once in the document:
\\nfunction App() {
return <>
<ComponentOne />
...
<ComponentOne /> // won\'t lead to a duplicate stylesheet link in the DOM
</>
}
For users accustomed to loading stylesheets manually this is an opportunity to locate those stylesheets alongside the components that depend on them allowing for better local reasoning and an easier time ensuring you only load the stylesheets that you actually depend on.
\\nStyle libraries and style integrations with bundlers can also adopt this new capability so even if you don’t directly render your own stylesheets, you can still benefit as your tools are upgraded to use this feature.
\\nFor more details, read the docs for <link>
and <style>
.
In HTML normal scripts (<script src=\\"...\\">
) and deferred scripts (<script defer=\\"\\" src=\\"...\\">
) load in document order which makes rendering these kinds of scripts deep within your component tree challenging. Async scripts (<script async=\\"\\" src=\\"...\\">
) however will load in arbitrary order.
In React 19 we’ve included better support for async scripts by allowing you to render them anywhere in your component tree, inside the components that actually depend on the script, without having to manage relocating and deduplicating script instances.
\\nfunction MyComponent() {
return (
<div>
<script async={true} src=\\"...\\" />
Hello World
</div>
)
}
function App() {
<html>
<body>
<MyComponent>
...
<MyComponent> // won\'t lead to duplicate script in the DOM
</body>
</html>
}
In all rendering environments, async scripts will be deduplicated so that React will only load and execute the script once even if it is rendered by multiple different components.
\\nIn Server Side Rendering, async scripts will be included in the <head>
and prioritized behind more critical resources that block paint such as stylesheets, fonts, and image preloads.
For more details, read the docs for <script>
.
During initial document load and on client side updates, telling the Browser about resources that it will likely need to load as early as possible can have a dramatic effect on page performance.
\\nReact 19 includes a number of new APIs for loading and preloading Browser resources to make it as easy as possible to build great experiences that aren’t held back by inefficient resource loading.
\\nimport { prefetchDNS, preconnect, preload, preinit } from \'react-dom\'
function MyComponent() {
preinit(\'https://.../path/to/some/script.js\', {as: \'script\' }) // loads and executes this script eagerly
preload(\'https://.../path/to/font.woff\', { as: \'font\' }) // preloads this font
preload(\'https://.../path/to/stylesheet.css\', { as: \'style\' }) // preloads this stylesheet
prefetchDNS(\'https://...\') // when you may not actually request anything from this host
preconnect(\'https://...\') // when you will request something but aren\'t sure what
}
<!-- the above would result in the following DOM/HTML --\x3e
<html>
<head>
<!-- links/scripts are prioritized by their utility to early loading, not call order --\x3e
<link rel=\\"prefetch-dns\\" href=\\"https://...\\">
<link rel=\\"preconnect\\" href=\\"https://...\\">
<link rel=\\"preload\\" as=\\"font\\" href=\\"https://.../path/to/font.woff\\">
<link rel=\\"preload\\" as=\\"style\\" href=\\"https://.../path/to/stylesheet.css\\">
<script async=\\"\\" src=\\"https://.../path/to/some/script.js\\"></script>
</head>
<body>
...
</body>
</html>
These APIs can be used to optimize initial page loads by moving discovery of additional resources like fonts out of stylesheet loading. They can also make client updates faster by prefetching a list of resources used by an anticipated navigation and then eagerly preloading those resources on click or even on hover.
\\nFor more details see Resource Preloading APIs.
\\nWe’ve improved hydration to account for third-party scripts and browser extensions.
\\nWhen hydrating, if an element that renders on the client doesn’t match the element found in the HTML from the server, React will force a client re-render to fix up the content. Previously, if an element was inserted by third-party scripts or browser extensions, it would trigger a mismatch error and client render.
\\nIn React 19, unexpected tags in the <head>
and <body>
will be skipped over, avoiding the mismatch errors. If React needs to re-render the entire document due to an unrelated hydration mismatch, it will leave in place stylesheets inserted by third-party scripts and browser extensions.
We improved error handling in React 19 to remove duplication and provide options for handling caught and uncaught errors. For example, when there’s an error in render caught by an Error Boundary, previously React would throw the error twice (once for the original error, then again after failing to automatically recover), and then call console.error
with info about where the error occurred.
This resulted in three errors for every caught error:
\\nIn React 19, we log a single error with all the error information included:
\\nAdditionally, we’ve added two new root options to complement onRecoverableError
:
onCaughtError
: called when React catches an error in an Error Boundary.onUncaughtError
: called when an error is thrown and not caught by an Error Boundary.onRecoverableError
: called when an error is thrown and automatically recovered.For more info and examples, see the docs for createRoot
and hydrateRoot
.
React 19 adds full support for custom elements and passes all tests on Custom Elements Everywhere.
\\nIn past versions, using Custom Elements in React has been difficult because React treated unrecognized props as attributes rather than properties. In React 19, we’ve added support for properties that works on the client and during SSR with the following strategy:
\\nstring
, number
, or the value is true
. Props with non-primitive types like object
, symbol
, function
, or value false
will be omitted.Thanks to Joey Arhar for driving the design and implementation of Custom Element support in React.
\\nSee the React 19 Upgrade Guide for step-by-step instructions and a full list of breaking and notable changes.
\\nNote: this post was originally published 04/25/2024 and has been updated to 12/05/2024 with the stable release.
October 21, 2024 by Lauren Tan.
\\nThe React team is excited to share new updates:
react-compiler-runtime
package.At React Conf 2024, we announced the experimental release of React Compiler, a build-time tool that optimizes your React app through automatic memoization. You can find an introduction to React Compiler here.
\\nSince the first release, we’ve fixed numerous bugs reported by the React community, received several high quality bug fixes and contributions1 to the compiler, made the compiler more resilient to the broad diversity of JavaScript patterns, and have continued to roll out the compiler more widely at Meta.
\\nIn this post, we want to share what’s next for React Compiler.
\\nAt React India 2024, we shared an update on React Compiler. Today, we are excited to announce a new Beta release of React Compiler and ESLint plugin. New betas are published to npm using the @beta
tag.
To install React Compiler Beta:
\\nOr, if you’re using Yarn:
\\nYou can watch Sathya Gunasekaran’s talk at React India here:
\\n\\nReact Compiler’s eslint plugin helps developers proactively identify and correct Rules of React violations. We strongly recommend everyone use the linter today. The linter does not require that you have the compiler installed, so you can use it independently, even if you are not ready to try out the compiler.
\\nTo install the linter only:
\\nOr, if you’re using Yarn:
\\nUsing the linter helps identify Rules of React breakages, making it easier to adopt the compiler when it’s fully released.
\\nReact Compiler produces code that depends on runtime APIs added in React 19, but we’ve since added support for the compiler to also work with React 17 and 18. If you are not on React 19 yet, in the Beta release you can now try out React Compiler by specifying a minimum target
in your compiler config, and adding react-compiler-runtime
as a dependency. You can find docs on this here.
Our initial release was focused on identifying major issues with using the compiler in applications. We’ve gotten great feedback and have substantially improved the compiler since then. We’re now ready for broad feedback from the community, and for library authors to try out the compiler to improve performance and the developer experience of maintaining your library.
\\nReact Compiler can also be used to compile libraries. Because React Compiler needs to run on the original source code prior to any code transformations, it is not possible for an application’s build pipeline to compile the libraries they use. Hence, our recommendation is for library maintainers to independently compile and test their libraries with the compiler, and ship compiled code to npm.
\\nBecause your code is pre-compiled, users of your library will not need to have the compiler enabled in order to benefit from the automatic memoization applied to your library. If your library targets apps not yet on React 19, specify a minimum target
and add react-compiler-runtime
as a direct dependency. The runtime package will use the correct implementation of APIs depending on the application’s version, and polyfill the missing APIs if necessary.
You can find more docs on this here.
\\nWe previously announced the invite-only React Compiler Working Group at React Conf to provide feedback, ask questions, and collaborate on the compiler’s experimental release.
\\nFrom today, together with the Beta release of React Compiler, we are opening up Working Group membership to everyone. The goal of the React Compiler Working Group is to prepare the ecosystem for a smooth, gradual adoption of React Compiler by existing applications and libraries. Please continue to file bug reports in the React repo, but please leave feedback, ask questions, or share ideas in the Working Group discussion forum.
\\nThe core team will also use the discussions repo to share our research findings. As the Stable Release gets closer, any important information will also be posted on this forum.
\\nAt React Conf, we shared that our rollout of the compiler on Quest Store and Instagram were successful. Since then, we’ve deployed React Compiler across several more major web apps at Meta, including Facebook and Threads. That means if you’ve used any of these apps recently, you may have had your experience powered by the compiler. We were able to onboard these apps onto the compiler with few code changes required, in a monorepo with more than 100,000 React components.
\\nWe’ve seen notable performance improvements across all of these apps. As we’ve rolled out, we’re continuing to see results on the order of the wins we shared previously at ReactConf. These apps have already been heavily hand tuned and optimized by Meta engineers and React experts over the years, so even improvements on the order of a few percent are a huge win for us.
\\nWe also expected developer productivity wins from React Compiler. To measure this, we collaborated with our data science partners at Meta2 to conduct a thorough statistical analysis of the impact of manual memoization on productivity. Before rolling out the compiler at Meta, we discovered that only about 8% of React pull requests used manual memoization and that these pull requests took 31-46% longer to author3. This confirmed our intuition that manual memoization introduces cognitive overhead, and we anticipate that React Compiler will lead to more efficient code authoring and review. Notably, React Compiler also ensures that all code is memoized by default, not just the (in our case) 8% where developers explicitly apply memoization.
\\nThis is not a final roadmap, and is subject to change.
\\nWe intend to ship a Release Candidate of the compiler in the near future following the Beta release, when the majority of apps and libraries that follow the Rules of React have been proven to work well with the compiler. After a period of final feedback from the community, we plan on a Stable Release for the compiler. The Stable Release will mark the beginning of a new foundation for React, and all apps and libraries will be strongly recommended to use the compiler and eslint plugin.
\\nThese releases also include the compiler’s eslint plugin, which surfaces diagnostics statically analyzed by the compiler. We plan to combine the existing eslint-plugin-react-hooks plugin with the compiler’s eslint plugin, so only one plugin needs to be installed.
\\nPost-Stable, we plan to add more compiler optimizations and improvements. This includes both continual improvements to automatic memoization, and new optimizations altogether, with minimal to no change of product code. Upgrading to each new release of the compiler is aimed to be straightforward, and each upgrade will continue to improve performance and add better handling of diverse JavaScript and React patterns.
\\nThroughout this process, we also plan to prototype an IDE extension for React. It is still very early in research, so we expect to be able to share more of our findings with you in a future React Labs blog post.
\\nThanks to Sathya Gunasekaran, Joe Savona, Ricky Hanlon, Alex Taylor, Jason Bonta, and Eli White for reviewing and editing this post.
\\nThanks @nikeee, @henryqdineen, @TrickyPi, and several others for their contributions to the compiler. ↩
\\nThanks Vaishali Garg for leading this study on React Compiler at Meta, and for reviewing this post. ↩
\\nAfter controlling on author tenure, diff length/complexity, and other potential confounding factors. ↩
\\nMay 22, 2024 by Ricky Hanlon.
\\nLast week we hosted React Conf 2024, a two-day conference in Henderson, Nevada where 700+ attendees gathered in-person to discuss the latest in UI engineering. This was our first in-person conference since 2019, and we were thrilled to be able to bring the community together again.
At React Conf 2024, we announced the React 19 RC, the React Native New Architecture Beta, and an experimental release of the React Compiler. The community also took the stage to announce React Router v7, Universal Server Components in Expo Router, React Server Components in RedwoodJS, and much more.
\\nThe entire day 1 and day 2 streams are available online. In this post, we’ll summarize the talks and announcements from the event.
\\nWatch the full day 1 stream here.
\\nTo kick off day 1, Meta CTO Andrew “Boz” Bosworth shared a welcome message followed by an introduction by Seth Webster, who manages the React Org at Meta, and our MC Ashley Narcisse.
\\nIn the day 1 keynote, Joe Savona shared our goals and vision for React to make it easy for anyone to build great user experiences. Lauren Tan followed with a State of React, where she shared that React was downloaded over 1 billion times in 2023, and that 37% of new developers learn to program with React. Finally, she highlighted the work of the React community to make React, React.
\\nFor more, check out these talks from the community later in the conference:
\\nNext in the keynote, Josh Story and Andrew Clark shared new features coming in React 19, and announced the React 19 RC which is ready for testing in production. Check out all the features in the React 19 release post, and see these talks for deep dives on the new features:
\\nFinally, we ended the keynote with Joe Savona, Sathya Gunasekaran, and Mofei Zhang announcing that the React Compiler is now Open Source, and sharing an experimental version of the React Compiler to try out.
\\nFor more information on using the Compiler and how it works, check out the docs and these talks:
\\nWatch the full day 1 keynote here:
\\n\\nWatch the full day 2 stream here.
\\nTo kick off day 2, Seth Webster shared a welcome message, followed by a Thank You from Eli White and an introduction by our Chief Vibes Officer Ashley Narcisse.
\\nIn the day 2 keynote, Nicola Corti shared the State of React Native, including 78 million downloads in 2023. He also highlighted apps using React Native including 2000+ screens used inside of Meta; the product details page in Facebook Marketplace, which is visited more than 2 billion times per day; and part of the Microsoft Windows Start Menu and some features in almost every Microsoft Office product across mobile and desktop.
\\nNicola also highlighted all the work the community does to support React Native including libraries, frameworks, and multiple platforms. For more, check out these talks from the community:
\\nRiccardo Cipolleschi continued the day 2 keynote by announcing that the React Native New Architecture is now in Beta and ready for apps to adopt in production. He shared new features and improvements in the new architecture, and shared the roadmap for the future of React Native. For more check out:
\\nNext in the keynote, Nicola announced that we are now recommending starting with a framework like Expo for all new apps created with React Native. With the change, he also announced a new React Native homepage and new Getting Started docs. You can view the new Getting Started guide in the React Native docs.
\\nFinally, to end the keynote, Kadi Kraman shared the latest features and improvements in Expo, and how to get started developing with React Native using Expo.
\\nWatch the full day 2 keynote here:
\\n\\nThe React and React Native teams also ended each day with a Q&A session:
\\nWe also heard talks on accessibility, error reporting, css, and more:
\\nThank you to all the staff, speakers, and participants who made React Conf 2024 possible. There are too many to list, but we want to thank a few in particular.
\\nThank you to Barbara Markiewicz, the team at Callstack, and our React Team Developer Advocate Matt Carroll for helping to plan the entire event; and to Sunny Leggett and everyone from Zero Slope for helping to organize the event.
\\nThank you Ashley Narcisse for being our MC and Chief Vibes Officer; and to Michael Chan and Jamon Holmgren for hosting the Q&A sessions.
\\nThank you Seth Webster and Eli White for welcoming us each day and providing direction on structure and content; and to Tom Occhino for joining us with a special message during the after-party.
\\nThank you Ricky Hanlon for providing detailed feedback on talks, working on slide designs, and generally filling in the gaps to sweat the details.
\\nThank you Callstack for building the conference website; and to Kadi Kraman and the Expo team for building the conference mobile app.
\\nThank you to all the sponsors who made the event possible: Remix, Amazon, MUI, Sentry, Abbott, Expo, RedwoodJS, and Vercel.
\\nThank you to the AV Team for the visuals, stage, and sound; and to the Westin Hotel for hosting us.
\\nThank you to all the speakers who shared their knowledge and experiences with the community.
\\nFinally, thank you to everyone who attended in person and online to show what makes React, React. React is more than a library, it is a community, and it was inspiring to see everyone come together to share and learn together.
\\nSee you next time!
April 25, 2024 by The React Team
\\nReact 19 RC is now available on npm!
In our React 19 RC Upgrade Guide, we shared step-by-step instructions for upgrading your app to React 19. In this post, we’ll give an overview of the new features in React 19, and how you can adopt them.
\\n\\nFor a list of breaking changes, see the Upgrade Guide.
\\nA common use case in React apps is to perform a data mutation and then update state in response. For example, when a user submits a form to change their name, you will make an API request, and then handle the response. In the past, you would need to handle pending states, errors, optimistic updates, and sequential requests manually.
\\nFor example, you could handle the pending and error state in useState
:
// Before Actions
function UpdateName({}) {
const [name, setName] = useState(\\"\\");
const [error, setError] = useState(null);
const [isPending, setIsPending] = useState(false);
const handleSubmit = async () => {
setIsPending(true);
const error = await updateName(name);
setIsPending(false);
if (error) {
setError(error);
return;
}
redirect(\\"/path\\");
};
return (
<div>
<input value={name} onChange={(event) => setName(event.target.value)} />
<button onClick={handleSubmit} disabled={isPending}>
Update
</button>
{error && <p>{error}</p>}
</div>
);
}
In React 19, we’re adding support for using async functions in transitions to handle pending states, errors, forms, and optimistic updates automatically.
\\nFor example, you can use useTransition
to handle the pending state for you:
// Using pending state from Actions
function UpdateName({}) {
const [name, setName] = useState(\\"\\");
const [error, setError] = useState(null);
const [isPending, startTransition] = useTransition();
const handleSubmit = () => {
startTransition(async () => {
const error = await updateName(name);
if (error) {
setError(error);
return;
}
redirect(\\"/path\\");
})
};
return (
<div>
<input value={name} onChange={(event) => setName(event.target.value)} />
<button onClick={handleSubmit} disabled={isPending}>
Update
</button>
{error && <p>{error}</p>}
</div>
);
}
The async transition will immediately set the isPending
state to true, make the async request(s), and switch isPending
to false after any transitions. This allows you to keep the current UI responsive and interactive while the data is changing.
Actions automatically manage submitting data for you:
useOptimistic
hook so you can show users instant feedback while the requests are submitting.<form>
elements now support passing functions to the action
and formAction
props. Passing functions to the action
props use Actions by default and reset the form automatically after submission.Building on top of Actions, React 19 introduces useOptimistic
to manage optimistic updates, and a new hook React.useActionState
to handle common cases for Actions. In react-dom
we’re adding <form>
Actions to manage forms automatically and useFormStatus
to support the common cases for Actions in forms.
In React 19, the above example can be simplified to:
\\n// Using <form> Actions and useActionState
function ChangeName({ name, setName }) {
const [error, submitAction, isPending] = useActionState(
async (previousState, formData) => {
const error = await updateName(formData.get(\\"name\\"));
if (error) {
return error;
}
redirect(\\"/path\\");
return null;
},
null,
);
return (
<form action={submitAction}>
<input type=\\"text\\" name=\\"name\\" />
<button type=\\"submit\\" disabled={isPending}>Update</button>
{error && <p>{error}</p>}
</form>
);
}
In the next section, we’ll break down each of the new Action features in React 19.
\\nuseActionState
To make the common cases easier for Actions, we’ve added a new hook called useActionState
:
const [error, submitAction, isPending] = useActionState(
async (previousState, newName) => {
const error = await updateName(newName);
if (error) {
// You can return any result of the action.
// Here, we return only the error.
return error;
}
// handle success
return null;
},
null,
);
useActionState
accepts a function (the “Action”), and returns a wrapped Action to call. This works because Actions compose. When the wrapped Action is called, useActionState
will return the last result of the Action as data
, and the pending state of the Action as pending
.
React.useActionState
was previously called ReactDOM.useFormState
in the Canary releases, but we’ve renamed it and deprecated useFormState
.
See #28491 for more info.
For more information, see the docs for useActionState
.
<form>
Actions Actions are also integrated with React 19’s new <form>
features for react-dom
. We’ve added support for passing functions as the action
and formAction
props of <form>
, <input>
, and <button>
elements to automatically submit forms with Actions:
<form action={actionFunction}>
When a <form>
Action succeeds, React will automatically reset the form for uncontrolled components. If you need to reset the <form>
manually, you can call the new requestFormReset
React DOM API.
For more information, see the react-dom
docs for <form>
, <input>
, and <button>
.
useFormStatus
In design systems, it’s common to write design components that need access to information about the <form>
they’re in, without drilling props down to the component. This can be done via Context, but to make the common case easier, we’ve added a new hook useFormStatus
:
import {useFormStatus} from \'react-dom\';
function DesignButton() {
const {pending} = useFormStatus();
return <button type=\\"submit\\" disabled={pending} />
}
useFormStatus
reads the status of the parent <form>
as if the form was a Context provider.
For more information, see the react-dom
docs for useFormStatus
.
useOptimistic
Another common UI pattern when performing a data mutation is to show the final state optimistically while the async request is underway. In React 19, we’re adding a new hook called useOptimistic
to make this easier:
function ChangeName({currentName, onUpdateName}) {
const [optimisticName, setOptimisticName] = useOptimistic(currentName);
const submitAction = async formData => {
const newName = formData.get(\\"name\\");
setOptimisticName(newName);
const updatedName = await updateName(newName);
onUpdateName(updatedName);
};
return (
<form action={submitAction}>
<p>Your name is: {optimisticName}</p>
<p>
<label>Change Name:</label>
<input
type=\\"text\\"
name=\\"name\\"
disabled={currentName !== optimisticName}
/>
</p>
</form>
);
}
The useOptimistic
hook will immediately render the optimisticName
while the updateName
request is in progress. When the update finishes or errors, React will automatically switch back to the currentName
value.
For more information, see the docs for useOptimistic
.
use
In React 19 we’re introducing a new API to read resources in render: use
.
For example, you can read a promise with use
, and React will Suspend until the promise resolves:
import {use} from \'react\';
function Comments({commentsPromise}) {
// `use` will suspend until the promise resolves.
const comments = use(commentsPromise);
return comments.map(comment => <p key={comment.id}>{comment}</p>);
}
function Page({commentsPromise}) {
// When `use` suspends in Comments,
// this Suspense boundary will be shown.
return (
<Suspense fallback={<div>Loading...</div>}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
)
}
use
does not support promises created in render. If you try to pass a promise created in render to use
, React will warn:
To fix, you need to pass a promise from a suspense powered library or framework that supports caching for promises. In the future we plan to ship features to make it easier to cache promises in render.
You can also read context with use
, allowing you to read Context conditionally such as after early returns:
import {use} from \'react\';
import ThemeContext from \'./ThemeContext\'
function Heading({children}) {
if (children == null) {
return null;
}
// This would not work with useContext
// because of the early return.
const theme = use(ThemeContext);
return (
<h1 style={{color: theme.color}}>
{children}
</h1>
);
}
The use
API can only be called in render, similar to hooks. Unlike hooks, use
can be called conditionally. In the future we plan to support more ways to consume resources in render with use
.
For more information, see the docs for use
.
Server Components are a new option that allows rendering components ahead of time, before bundling, in an environment separate from your client application or SSR server. This separate environment is the “server” in React Server Components. Server Components can run once at build time on your CI server, or they can be run for each request using a web server.
\\nReact 19 includes all of the React Server Components features included from the Canary channel. This means libraries that ship with Server Components can now target React 19 as a peer dependency with a react-server
export condition for use in frameworks that support the Full-stack React Architecture.
While React Server Components in React 19 are stable and will not break between major versions, the underlying APIs used to implement a React Server Components bundler or framework do not follow semver and may break between minors in React 19.x.
To support React Server Components as a bundler or framework, we recommend pinning to a specific React version, or using the Canary release. We will continue working with bundlers and frameworks to stabilize the APIs used to implement React Server Components in the future.
For more, see the docs for React Server Components.
\\nServer Actions allow Client Components to call async functions executed on the server.
\\nWhen a Server Action is defined with the \\"use server\\"
directive, your framework will automatically create a reference to the server function, and pass that reference to the Client Component. When that function is called on the client, React will send a request to the server to execute the function, and return the result.
A common misunderstanding is that Server Components are denoted by \\"use server\\"
, but there is no directive for Server Components. The \\"use server\\"
directive is used for Server Actions.
For more info, see the docs for Directives.
Server Actions can be created in Server Components and passed as props to Client Components, or they can be imported and used in Client Components.
\\nFor more, see the docs for React Server Actions.
\\nref
as a prop Starting in React 19, you can now access ref
as a prop for function components:
function MyInput({placeholder, ref}) {
return <input placeholder={placeholder} ref={ref} />
}
//...
<MyInput ref={ref} />
New function components will no longer need forwardRef
, and we will be publishing a codemod to automatically update your components to use the new ref
prop. In future versions we will deprecate and remove forwardRef
.
refs
passed to classes are not passed as props since they reference the component instance.
We also improved error reporting for hydration errors in react-dom
. For example, instead of logging multiple errors in DEV without any information about the mismatch:
We now log a single message with a diff of the mismatch:
\\nif (typeof window !== \'undefined\')
.\\n- Variable input such as Date.now()
or Math.random()
which changes each time it’s called.\\n- Date formatting in a user’s locale which doesn’t match the server.\\n- External changing data without sending a snapshot of it along with the HTML.\\n- Invalid HTML tag nesting.\\n\\nIt can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\\n\\nhttps://react.dev/link/hydration-mismatch \\n\\n <App>\\n <span>\\n+ Client\\n- Server\\n\\n at throwOnHydrationMismatch\\n …<Context>
as a provider In React 19, you can render <Context>
as a provider instead of <Context.Provider>
:
const ThemeContext = createContext(\'\');
function App({children}) {
return (
<ThemeContext value=\\"dark\\">
{children}
</ThemeContext>
);
}
New Context providers can use <Context>
and we will be publishing a codemod to convert existing providers. In future versions we will deprecate <Context.Provider>
.
We now support returning a cleanup function from ref
callbacks:
<input
ref={(ref) => {
// ref created
// NEW: return a cleanup function to reset
// the ref when element is removed from DOM.
return () => {
// ref cleanup
};
}}
/>
When the component unmounts, React will call the cleanup function returned from the ref
callback. This works for DOM refs, refs to class components, and useImperativeHandle
.
Previously, React would call ref
functions with null
when unmounting the component. If your ref
returns a cleanup function, React will now skip this step.
In future versions, we will deprecate calling refs with null
when unmounting components.
Due to the introduction of ref cleanup functions, returning anything else from a ref
callback will now be rejected by TypeScript. The fix is usually to stop using implicit returns, for example:
- <div ref={current => (instance = current)} />
+ <div ref={current => {instance = current}} />
The original code returned the instance of the HTMLDivElement
and TypeScript wouldn’t know if this was supposed to be a cleanup function or if you didn’t want to return a cleanup function.
You can codemod this pattern with no-implicit-ref-callback-return
.
useDeferredValue
initial value We’ve added an initialValue
option to useDeferredValue
:
function Search({deferredValue}) {
// On initial render the value is \'\'.
// Then a re-render is scheduled with the deferredValue.
const value = useDeferredValue(deferredValue, \'\');
return (
<Results query={value} />
);
}
When initialValue is provided, useDeferredValue
will return it as value
for the initial render of the component, and schedules a re-render in the background with the deferredValue returned.
For more, see useDeferredValue
.
In HTML, document metadata tags like <title>
, <link>
, and <meta>
are reserved for placement in the <head>
section of the document. In React, the component that decides what metadata is appropriate for the app may be very far from the place where you render the <head>
or React does not render the <head>
at all. In the past, these elements would need to be inserted manually in an effect, or by libraries like react-helmet
, and required careful handling when server rendering a React application.
In React 19, we’re adding support for rendering document metadata tags in components natively:
\\nfunction BlogPost({post}) {
return (
<article>
<h1>{post.title}</h1>
<title>{post.title}</title>
<meta name=\\"author\\" content=\\"Josh\\" />
<link rel=\\"author\\" href=\\"https://twitter.com/joshcstory/\\" />
<meta name=\\"keywords\\" content={post.keywords} />
<p>
Eee equals em-see-squared...
</p>
</article>
);
}
When React renders this component, it will see the <title>
<link>
and <meta>
tags, and automatically hoist them to the <head>
section of document. By supporting these metadata tags natively, we’re able to ensure they work with client-only apps, streaming SSR, and Server Components.
For simple use cases, rendering Document Metadata as tags may be suitable, but libraries can offer more powerful features like overriding generic metadata with specific metadata based on the current route. These features make it easier for frameworks and libraries like react-helmet
to support metadata tags, rather than replace them.
For more info, see the docs for <title>
, <link>
, and <meta>
.
Stylesheets, both externally linked (<link rel=\\"stylesheet\\" href=\\"...\\">
) and inline (<style>...</style>
), require careful positioning in the DOM due to style precedence rules. Building a stylesheet capability that allows for composability within components is hard, so users often end up either loading all of their styles far from the components that may depend on them, or they use a style library which encapsulates this complexity.
In React 19, we’re addressing this complexity and providing even deeper integration into Concurrent Rendering on the Client and Streaming Rendering on the Server with built in support for stylesheets. If you tell React the precedence
of your stylesheet it will manage the insertion order of the stylesheet in the DOM and ensure that the stylesheet (if external) is loaded before revealing content that depends on those style rules.
function ComponentOne() {
return (
<Suspense fallback=\\"loading...\\">
<link rel=\\"stylesheet\\" href=\\"foo\\" precedence=\\"default\\" />
<link rel=\\"stylesheet\\" href=\\"bar\\" precedence=\\"high\\" />
<article class=\\"foo-class bar-class\\">
{...}
</article>
</Suspense>
)
}
function ComponentTwo() {
return (
<div>
<p>{...}</p>
<link rel=\\"stylesheet\\" href=\\"baz\\" precedence=\\"default\\" /> <-- will be inserted between foo & bar
</div>
)
}
During Server Side Rendering React will include the stylesheet in the <head>
, which ensures that the browser will not paint until it has loaded. If the stylesheet is discovered late after we’ve already started streaming, React will ensure that the stylesheet is inserted into the <head>
on the client before revealing the content of a Suspense boundary that depends on that stylesheet.
During Client Side Rendering React will wait for newly rendered stylesheets to load before committing the render. If you render this component from multiple places within your application React will only include the stylesheet once in the document:
\\nfunction App() {
return <>
<ComponentOne />
...
<ComponentOne /> // won\'t lead to a duplicate stylesheet link in the DOM
</>
}
For users accustomed to loading stylesheets manually this is an opportunity to locate those stylesheets alongside the components that depend on them allowing for better local reasoning and an easier time ensuring you only load the stylesheets that you actually depend on.
\\nStyle libraries and style integrations with bundlers can also adopt this new capability so even if you don’t directly render your own stylesheets, you can still benefit as your tools are upgraded to use this feature.
\\nFor more details, read the docs for <link>
and <style>
.
In HTML normal scripts (<script src=\\"...\\">
) and deferred scripts (<script defer=\\"\\" src=\\"...\\">
) load in document order which makes rendering these kinds of scripts deep within your component tree challenging. Async scripts (<script async=\\"\\" src=\\"...\\">
) however will load in arbitrary order.
In React 19 we’ve included better support for async scripts by allowing you to render them anywhere in your component tree, inside the components that actually depend on the script, without having to manage relocating and deduplicating script instances.
\\nfunction MyComponent() {
return (
<div>
<script async={true} src=\\"...\\" />
Hello World
</div>
)
}
function App() {
<html>
<body>
<MyComponent>
...
<MyComponent> // won\'t lead to duplicate script in the DOM
</body>
</html>
}
In all rendering environments, async scripts will be deduplicated so that React will only load and execute the script once even if it is rendered by multiple different components.
\\nIn Server Side Rendering, async scripts will be included in the <head>
and prioritized behind more critical resources that block paint such as stylesheets, fonts, and image preloads.
For more details, read the docs for <script>
.
During initial document load and on client side updates, telling the Browser about resources that it will likely need to load as early as possible can have a dramatic effect on page performance.
\\nReact 19 includes a number of new APIs for loading and preloading Browser resources to make it as easy as possible to build great experiences that aren’t held back by inefficient resource loading.
\\nimport { prefetchDNS, preconnect, preload, preinit } from \'react-dom\'
function MyComponent() {
preinit(\'https://.../path/to/some/script.js\', {as: \'script\' }) // loads and executes this script eagerly
preload(\'https://.../path/to/font.woff\', { as: \'font\' }) // preloads this font
preload(\'https://.../path/to/stylesheet.css\', { as: \'style\' }) // preloads this stylesheet
prefetchDNS(\'https://...\') // when you may not actually request anything from this host
preconnect(\'https://...\') // when you will request something but aren\'t sure what
}
<!-- the above would result in the following DOM/HTML --\x3e
<html>
<head>
<!-- links/scripts are prioritized by their utility to early loading, not call order --\x3e
<link rel=\\"prefetch-dns\\" href=\\"https://...\\">
<link rel=\\"preconnect\\" href=\\"https://...\\">
<link rel=\\"preload\\" as=\\"font\\" href=\\"https://.../path/to/font.woff\\">
<link rel=\\"preload\\" as=\\"style\\" href=\\"https://.../path/to/stylesheet.css\\">
<script async=\\"\\" src=\\"https://.../path/to/some/script.js\\"></script>
</head>
<body>
...
</body>
</html>
These APIs can be used to optimize initial page loads by moving discovery of additional resources like fonts out of stylesheet loading. They can also make client updates faster by prefetching a list of resources used by an anticipated navigation and then eagerly preloading those resources on click or even on hover.
\\nFor more details see Resource Preloading APIs.
\\nWe’ve improved hydration to account for third-party scripts and browser extensions.
\\nWhen hydrating, if an element that renders on the client doesn’t match the element found in the HTML from the server, React will force a client re-render to fix up the content. Previously, if an element was inserted by third-party scripts or browser extensions, it would trigger a mismatch error and client render.
\\nIn React 19, unexpected tags in the <head>
and <body>
will be skipped over, avoiding the mismatch errors. If React needs to re-render the entire document due to an unrelated hydration mismatch, it will leave in place stylesheets inserted by third-party scripts and browser extensions.
We improved error handling in React 19 to remove duplication and provide options for handling caught and uncaught errors. For example, when there’s an error in render caught by an Error Boundary, previously React would throw the error twice (once for the original error, then again after failing to automatically recover), and then call console.error
with info about where the error occurred.
This resulted in three errors for every caught error:
\\nIn React 19, we log a single error with all the error information included:
\\nAdditionally, we’ve added two new root options to complement onRecoverableError
:
onCaughtError
: called when React catches an error in an Error Boundary.onUncaughtError
: called when an error is thrown and not caught by an Error Boundary.onRecoverableError
: called when an error is thrown and automatically recovered.For more info and examples, see the docs for createRoot
and hydrateRoot
.
React 19 adds full support for custom elements and passes all tests on Custom Elements Everywhere.
\\nIn past versions, using Custom Elements in React has been difficult because React treated unrecognized props as attributes rather than properties. In React 19, we’ve added support for properties that works on the client and during SSR with the following strategy:
\\nstring
, number
, or the value is true
. Props with non-primitive types like object
, symbol
, function
, or value false
will be omitted.Thanks to Joey Arhar for driving the design and implementation of Custom Element support in React.
\\nSee the React 19 Upgrade Guide for step-by-step instructions and a full list of breaking and notable changes.
April 25, 2024 by Ricky Hanlon
\\nThe improvements added to React 19 RC require some breaking changes, but we’ve worked to make the upgrade as smooth as possible, and we don’t expect the changes to impact most apps.
To help make the upgrade to React 19 easier, we’ve published a react@18.3
release that is identical to 18.2 but adds warnings for deprecated APIs and other changes that are needed for React 19.
We recommend upgrading to React 18.3 first to help identify any issues before upgrading to React 19.
For a list of changes in 18.3 see the Release Notes.
In this post, we will guide you through the steps for upgrading to React 19:
\\nIf you’d like to help us test React 19, follow the steps in this upgrade guide and report any issues you encounter. For a list of new features added to React 19, see the React 19 release post.
\\nWe introduced a new JSX transform in 2020 to improve bundle size and use JSX without importing React. In React 19, we’re adding additional improvements like using ref as a prop and JSX speed improvements that require the new transform.
If the new transform is not enabled, you will see this warning:
We expect most apps will not be affected since the transform is enabled in most environments already. For manual instructions on how to upgrade, please see the announcement post.
To install the latest version of React and React DOM:
\\nnpm install --save-exact react@rc react-dom@rc
Or, if you’re using Yarn:
\\nyarn add --exact react@rc react-dom@rc
If you’re using TypeScript, you also need to update the types. Once React 19 is released as stable, you can install the types as usual from @types/react
and @types/react-dom
. Until the stable release, the types are available in different packages which need to be enforced in your package.json
:
{
\\"dependencies\\": {
\\"@types/react\\": \\"npm:types-react@rc\\",
\\"@types/react-dom\\": \\"npm:types-react-dom@rc\\"
},
\\"overrides\\": {
\\"@types/react\\": \\"npm:types-react@rc\\",
\\"@types/react-dom\\": \\"npm:types-react-dom@rc\\"
}
}
We’re also including a codemod for the most common replacements. See TypeScript changes below.
\\nTo help with the upgrade, we’ve worked with the team at codemod.com to publish codemods that will automatically update your code to many of the new APIs and patterns in React 19.
\\nAll codemods are available in the react-codemod
repo and the Codemod team have joined in helping maintain the codemods. To run these codemods, we recommend using the codemod
command instead of the react-codemod
because it runs faster, handles more complex code migrations, and provides better support for TypeScript.
Run all codemods listed in this guide with the React 19 codemod
recipe:
npx codemod@latest react/19/migration-recipe
This will run the following codemods from react-codemod
:
replace-reactdom-render
replace-string-ref
replace-act-import
replace-use-form-state
prop-types-typescript
This does not include the TypeScript changes. See TypeScript changes below.
Changes that include a codemod include the command below.
\\nFor a list of all available codemods, see the react-codemod
repo.
In previous versions of React, errors thrown during render were caught and rethrown. In DEV, we would also log to console.error
, resulting in duplicate error logs.
In React 19, we’ve improved how errors are handled to reduce duplication by not re-throwing:
\\nwindow.reportError
.console.error
.This change should not impact most apps, but if your production error reporting relies on errors being re-thrown, you may need to update your error handling. To support this, we’ve added new methods to createRoot
and hydrateRoot
for custom error handling:
const root = createRoot(container, {
onUncaughtError: (error, errorInfo) => {
// ... log error report
},
onCaughtError: (error, errorInfo) => {
// ... log error report
}
});
For more info, see the docs for createRoot
and hydrateRoot
.
propTypes
and defaultProps
for functions PropTypes
were deprecated in April 2017 (v15.5.0).
In React 19, we’re removing the propType
checks from the React package, and using them will be silently ignored. If you’re using propTypes
, we recommend migrating to TypeScript or another type-checking solution.
We’re also removing defaultProps
from function components in place of ES6 default parameters. Class components will continue to support defaultProps
since there is no ES6 alternative.
// Before
import PropTypes from \'prop-types\';
function Heading({text}) {
return <h1>{text}</h1>;
}
Heading.propTypes = {
text: PropTypes.string,
};
Heading.defaultProps = {
text: \'Hello, world!\',
};
// After
interface Props {
text?: string;
}
function Heading({text = \'Hello, world!\'}: Props) {
return <h1>{text}</h1>;
}
Codemod propTypes
to TypeScript with:
npx codemod@latest react/prop-types-typescript
contextTypes
and getChildContext
Legacy Context was deprecated in October 2018 (v16.6.0).
\\nLegacy Context was only available in class components using the APIs contextTypes
and getChildContext
, and was replaced with contextType
due to subtle bugs that were easy to miss. In React 19, we’re removing Legacy Context to make React slightly smaller and faster.
If you’re still using Legacy Context in class components, you’ll need to migrate to the new contextType
API:
// Before
import PropTypes from \'prop-types\';
class Parent extends React.Component {
static childContextTypes = {
foo: PropTypes.string.isRequired,
};
getChildContext() {
return { foo: \'bar\' };
}
render() {
return <Child />;
}
}
class Child extends React.Component {
static contextTypes = {
foo: PropTypes.string.isRequired,
};
render() {
return <div>{this.context.foo}</div>;
}
}
// After
const FooContext = React.createContext();
class Parent extends React.Component {
render() {
return (
<FooContext value=\'bar\'>
<Child />
</FooContext>
);
}
}
class Child extends React.Component {
static contextType = FooContext;
render() {
return <div>{this.context}</div>;
}
}
String refs were deprecated in March, 2018 (v16.3.0).
\\nClass components supported string refs before being replaced by ref callbacks due to multiple downsides. In React 19, we’re removing string refs to make React simpler and easier to understand.
\\nIf you’re still using string refs in class components, you’ll need to migrate to ref callbacks:
\\n// Before
class MyComponent extends React.Component {
componentDidMount() {
this.refs.input.focus();
}
render() {
return <input ref=\'input\' />;
}
}
// After
class MyComponent extends React.Component {
componentDidMount() {
this.input.focus();
}
render() {
return <input ref={input => this.input = input} />;
}
}
Codemod string refs with ref
callbacks:
npx codemod@latest react/19/replace-string-ref
Module pattern factories were deprecated in August 2019 (v16.9.0).
\\nThis pattern was rarely used and supporting it causes React to be slightly larger and slower than necessary. In React 19, we’re removing support for module pattern factories, and you’ll need to migrate to regular functions:
\\n// Before
function FactoryComponent() {
return { render() { return <div />; } }
}
// After
function FactoryComponent() {
return <div />;
}
React.createFactory
createFactory
was deprecated in February 2020 (v16.13.0).
Using createFactory
was common before broad support for JSX, but it’s rarely used today and can be replaced with JSX. In React 19, we’re removing createFactory
and you’ll need to migrate to JSX:
// Before
import { createFactory } from \'react\';
const button = createFactory(\'button\');
// After
const button = <button />;
react-test-renderer/shallow
In React 18, we updated react-test-renderer/shallow
to re-export react-shallow-renderer. In React 19, we’re removing react-test-render/shallow
to prefer installing the package directly:
npm install react-shallow-renderer --save-dev
- import ShallowRenderer from \'react-test-renderer/shallow\';
+ import ShallowRenderer from \'react-shallow-renderer\';
Shallow rendering depends on React internals and can block you from future upgrades. We recommend migrating your tests to @testing-library/react or @testing-library/react-native.
react-dom/test-utils
We’ve moved act
from react-dom/test-utils
to the react
package:
ReactDOMTestUtils.act
is deprecated in favor of React.act
. Import act
from react
instead of react-dom/test-utils
. See https://react.dev/warnings/react-dom-test-utils for more info.To fix this warning, you can import act
from react
:
- import {act} from \'react-dom/test-utils\'
+ import {act} from \'react\';
All other test-utils
functions have been removed. These utilities were uncommon, and made it too easy to depend on low level implementation details of your components and React. In React 19, these functions will error when called and their exports will be removed in a future version.
See the warning page for alternatives.
\\nCodemod ReactDOMTestUtils.act
to React.act
:
npx codemod@latest react/19/replace-act-import
ReactDOM.render
ReactDOM.render
was deprecated in March 2022 (v18.0.0). In React 19, we’re removing ReactDOM.render
and you’ll need to migrate to using ReactDOM.createRoot
:
// Before
import {render} from \'react-dom\';
render(<App />, document.getElementById(\'root\'));
// After
import {createRoot} from \'react-dom/client\';
const root = createRoot(document.getElementById(\'root\'));
root.render(<App />);
Codemod ReactDOM.render
to ReactDOMClient.createRoot
:
npx codemod@latest react/19/replace-reactdom-render
ReactDOM.hydrate
ReactDOM.hydrate
was deprecated in March 2022 (v18.0.0). In React 19, we’re removing ReactDOM.hydrate
you’ll need to migrate to using ReactDOM.hydrateRoot
,
// Before
import {hydrate} from \'react-dom\';
hydrate(<App />, document.getElementById(\'root\'));
// After
import {hydrateRoot} from \'react-dom/client\';
hydrateRoot(document.getElementById(\'root\'), <App />);
Codemod ReactDOM.hydrate
to ReactDOMClient.hydrateRoot
:
npx codemod@latest react/19/replace-reactdom-render
unmountComponentAtNode
ReactDOM.unmountComponentAtNode
was deprecated in March 2022 (v18.0.0). In React 19, you’ll need to migrate to using root.unmount()
.
// Before
unmountComponentAtNode(document.getElementById(\'root\'));
// After
root.unmount();
For more see root.unmount()
for createRoot
and hydrateRoot
.
Codemod unmountComponentAtNode
to root.unmount
:
npx codemod@latest react/19/replace-reactdom-render
ReactDOM.findDOMNode
ReactDOM.findDOMNode
was deprecated in October 2018 (v16.6.0).
We’re removing findDOMNode
because it was a legacy escape hatch that was slow to execute, fragile to refactoring, only returned the first child, and broke abstraction levels (see more here). You can replace ReactDOM.findDOMNode
with DOM refs:
// Before
import {findDOMNode} from \'react-dom\';
function AutoselectingInput() {
useEffect(() => {
const input = findDOMNode(this);
input.select()
}, []);
return <input defaultValue=\\"Hello\\" />;
}
// After
function AutoselectingInput() {
const ref = useRef(null);
useEffect(() => {
ref.current.select();
}, []);
return <input ref={ref} defaultValue=\\"Hello\\" />
}
element.ref
React 19 supports ref
as a prop, so we’re deprecating the element.ref
in place of element.props.ref
.
Accessing element.ref
will warn:
react-test-renderer
We are deprecating react-test-renderer
because it implements its own renderer environment that doesn’t match the environment users use, promotes testing implementation details, and relies on introspection of React’s internals.
The test renderer was created before there were more viable testing strategies available like React Testing Library, and we now recommend using a modern testing library instead.
\\nIn React 19, react-test-renderer
logs a deprecation warning, and has switched to concurrent rendering. We recommend migrating your tests to @testing-library/react or @testing-library/react-native for a modern and well supported testing experience.
React 19 includes several fixes and improvements to Strict Mode.
\\nWhen double rendering in Strict Mode in development, useMemo
and useCallback
will reuse the memoized results from the first render during the second render. Components that are already Strict Mode compatible should not notice a difference in behavior.
As with all Strict Mode behaviors, these features are designed to proactively surface bugs in your components during development so you can fix them before they are shipped to production. For example, during development, Strict Mode will double-invoke ref callback functions on initial mount, to simulate what happens when a mounted component is replaced by a Suspense fallback.
\\nUMD was widely used in the past as a convenient way to load React without a build step. Now, there are modern alternatives for loading modules as scripts in HTML documents. Starting with React 19, React will no longer produce UMD builds to reduce the complexity of its testing and release process.
\\nTo load React 19 with a script tag, we recommend using an ESM-based CDN such as esm.sh.
\\n<script type=\\"module\\">
import React from \\"https://esm.sh/react@19/?dev\\"
import ReactDOMClient from \\"https://esm.sh/react-dom@19/client?dev\\"
...
</script>
This release includes changes to React internals that may impact libraries that ignore our pleas to not use internals like SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
. These changes are necessary to land improvements in React 19, and will not break libraries that follow our guidelines.
Based on our Versioning Policy, these updates are not listed as breaking changes, and we are not including docs for how to upgrade them. The recommendation is to remove any code that depends on internals.
\\nTo reflect the impact of using internals, we have renamed the SECRET_INTERNALS
suffix to:
_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE
In the future we will more aggressively block accessing internals from React to discourage usage and ensure users are not blocked from upgrading.
\\nWe’ve cleaned up the TypeScript types based on the removed APIs in React 19. Some of the removed have types been moved to more relevant packages, and others are no longer needed to describe React’s behavior.
\\nWe’ve published types-react-codemod
to migrate most type related breaking changes:
npx types-react-codemod@latest preset-19 ./path-to-app
If you have a lot of unsound access to element.props
, you can run this additional codemod:
npx types-react-codemod@latest react-element-default-any-props ./path-to-your-react-ts-files
Check out types-react-codemod
for a list of supported replacements. If you feel a codemod is missing, it can be tracked in the list of missing React 19 codemods.
ref
cleanups required This change is included in the react-19
codemod preset as no-implicit-ref-callback-return
.
Due to the introduction of ref cleanup functions, returning anything else from a ref callback will now be rejected by TypeScript. The fix is usually to stop using implicit returns:
\\n- <div ref={current => (instance = current)} />
+ <div ref={current => {instance = current}} />
The original code returned the instance of the HTMLDivElement
and TypeScript wouldn’t know if this was supposed to be a cleanup function or not.
useRef
requires an argument This change is included in the react-19
codemod preset as refobject-defaults
.
A long-time complaint of how TypeScript and React work has been useRef
. We’ve changed the types so that useRef
now requires an argument. This significantly simplifies its type signature. It’ll now behave more like createContext
.
// @ts-expect-error: Expected 1 argument but saw none
useRef();
// Passes
useRef(undefined);
// @ts-expect-error: Expected 1 argument but saw none
createContext();
// Passes
createContext(undefined);
This now also means that all refs are mutable. You’ll no longer hit the issue where you can’t mutate a ref because you initialised it with null
:
const ref = useRef<number>(null);
// Cannot assign to \'current\' because it is a read-only property
ref.current = 1;
MutableRef
is now deprecated in favor of a single RefObject
type which useRef
will always return:
interface RefObject<T> {
current: T
}
declare function useRef<T>: RefObject<T>
useRef
still has a convenience overload for useRef<T>(null)
that automatically returns RefObject<T | null>
. To ease migration due to the required argument for useRef
, a convenience overload for useRef(undefined)
was added that automatically returns RefObject<T | undefined>
.
Check out [RFC] Make all refs mutable for prior discussions about this change.
\\nReactElement
TypeScript type This change is included in the react-element-default-any-props
codemod.
The props
of React elements now default to unknown
instead of any
if the element is typed as ReactElement
. This does not affect you if you pass a type argument to ReactElement
:
type Example2 = ReactElement<{ id: string }>[\\"props\\"];
// ^? { id: string }
But if you relied on the default, you now have to handle unknown
:
type Example = ReactElement[\\"props\\"];
// ^? Before, was \'any\', now \'unknown\'
You should only need it if you have a lot of legacy code relying on unsound access of element props. Element introspection only exists as an escape hatch, and you should make it explicit that your props access is unsound via an explicit any
.
This change is included in the react-19
codemod preset as scoped-jsx
A long-time request is to remove the global JSX
namespace from our types in favor of React.JSX
. This helps prevent pollution of global types which prevents conflicts between different UI libraries that leverage JSX.
You’ll now need to wrap module augmentation of the JSX namespace in `declare module ”…”:
\\n// global.d.ts
+ declare module \\"react\\" {
namespace JSX {
interface IntrinsicElements {
\\"my-element\\": {
myElementProps: string;
};
}
}
+ }
The exact module specifier depends on the JSX runtime you specified in the compilerOptions
of your tsconfig.json
:
\\"jsx\\": \\"react-jsx\\"
it would be react/jsx-runtime
.\\"jsx\\": \\"react-jsxdev\\"
it would be react/jsx-dev-runtime
.\\"jsx\\": \\"react\\"
and \\"jsx\\": \\"preserve\\"
it would be react
.useReducer
typings useReducer
now has improved type inference thanks to @mfp22.
However, this required a breaking change where useReducer
doesn’t accept the full reducer type as a type parameter but instead either needs none (and rely on contextual typing) or needs both the state and action type.
The new best practice is not to pass type arguments to useReducer
.
- useReducer<React.Reducer<State, Action>>(reducer)
+ useReducer(reducer)
This may not work in edge cases where you can explicitly type the state and action, by passing in the Action
in a tuple:
- useReducer<React.Reducer<State, Action>>(reducer)
+ useReducer<State, [Action]>(reducer)
If you define the reducer inline, we encourage to annotate the function parameters instead:
\\n- useReducer<React.Reducer<State, Action>>((state, action) => state)
+ useReducer((state: State, action: Action) => state)
This is also what you’d also have to do if you move the reducer outside of the useReducer
call:
const reducer = (state: State, action: Action) => state;
errorInfo.digest
from onRecoverableError
#28222unstable_flushControlled
#26397unstable_createEventHandle
#28271unstable_renderSubtreeIntoContainer
#28271unstable_runWithPrioirty
#28271react-is
28224We’ll publish the full changelog with the stable release of React 19.
\\nThanks to Andrew Clark, Eli White, Jack Pope, Jan Kassens, Josh Story, Matt Carroll, Noah Lemen, Sophie Alpert, and Sebastian Silbermann for reviewing and editing this post.
February 15, 2024 by Joseph Savona, Ricky Hanlon, Andrew Clark, Matt Carroll, and Dan Abramov.
\\nIn React Labs posts, we write about projects in active research and development. We’ve made significant progress since our last update, and we’d like to share our progress.
React Conf 2024 is scheduled for May 15–16 in Henderson, Nevada! If you’re interested in attending React Conf in person, you can sign up for the ticket lottery until February 28th.
For more info on tickets, free streaming, sponsoring, and more, see the React Conf website.
React Compiler is no longer a research project: the compiler now powers instagram.com in production, and we are working to ship the compiler across additional surfaces at Meta and to prepare the first open source release.
\\nAs discussed in our previous post, React can sometimes re-render too much when state changes. Since the early days of React our solution for such cases has been manual memoization. In our current APIs, this means applying the useMemo
, useCallback
, and memo
APIs to manually tune how much React re-renders on state changes. But manual memoization is a compromise. It clutters up our code, is easy to get wrong, and requires extra work to keep up to date.
Manual memoization is a reasonable compromise, but we weren’t satisfied. Our vision is for React to automatically re-render just the right parts of the UI when state changes, without compromising on React’s core mental model. We believe that React’s approach — UI as a simple function of state, with standard JavaScript values and idioms — is a key part of why React has been approachable for so many developers. That’s why we’ve invested in building an optimizing compiler for React.
\\nJavaScript is a notoriously challenging language to optimize, thanks to its loose rules and dynamic nature. React Compiler is able to compile code safely by modeling both the rules of JavaScript and the “rules of React”. For example, React components must be idempotent — returning the same value given the same inputs — and can’t mutate props or state values. These rules limit what developers can do and help to carve out a safe space for the compiler to optimize.
\\nOf course, we understand that developers sometimes bend the rules a bit, and our goal is to make React Compiler work out of the box on as much code as possible. The compiler attempts to detect when code doesn’t strictly follow React’s rules and will either compile the code where safe or skip compilation if it isn’t safe. We’re testing against Meta’s large and varied codebase in order to help validate this approach.
\\nFor developers who are curious about making sure their code follows React’s rules, we recommend enabling Strict Mode and configuring React’s ESLint plugin. These tools can help to catch subtle bugs in your React code, improving the quality of your applications today, and future-proofs your applications for upcoming features such as React Compiler. We are also working on consolidated documentation of the rules of React and updates to our ESLint plugin to help teams understand and apply these rules to create more robust apps.
\\nTo see the compiler in action, you can check out our talk from last fall. At the time of the talk, we had early experimental data from trying React Compiler on one page of instagram.com. Since then, we shipped the compiler to production across instagram.com. We’ve also expanded our team to accelerate the rollout to additional surfaces at Meta and to open source. We’re excited about the path ahead and will have more to share in the coming months.
\\nWe previously shared that we were exploring solutions for sending data from the client to the server with Server Actions, so that you can execute database mutations and implement forms. During development of Server Actions, we extended these APIs to support data handling in client-only applications as well.
\\nWe refer to this broader collection of features as simply “Actions”. Actions allow you to pass a function to DOM elements such as <form/>
:
<form action={search}>
<input name=\\"query\\" />
<button type=\\"submit\\">Search</button>
</form>
The action
function can operate synchronously or asynchronously. You can define them on the client side using standard JavaScript or on the server with the \'use server\'
directive. When using an action, React will manage the life cycle of the data submission for you, providing hooks like useFormStatus
, and useActionState
to access the current state and response of the form action.
By default, Actions are submitted within a transition, keeping the current page interactive while the action is processing. Since Actions support async functions, we’ve also added the ability to use async/await
in transitions. This allows you to show pending UI with the isPending
state of a transition when an async request like fetch
starts, and show the pending UI all the way through the update being applied.
Alongside Actions, we’re introducing a feature named useOptimistic
for managing optimistic state updates. With this hook, you can apply temporary updates that are automatically reverted once the final state commits. For Actions, this allows you to optimistically set the final state of the data on the client, assuming the submission is successful, and revert to the value for data received from the server. It works using regular async
/await
, so it works the same whether you’re using fetch
on the client, or a Server Action from the server.
Library authors can implement custom action={fn}
props in their own components with useTransition
. Our intent is for libraries to adopt the Actions pattern when designing their component APIs, to provide a consistent experience for React developers. For example, if your library provides a <Calendar onSelect={eventHandler}>
component, consider also exposing a <Calendar selectAction={action}>
API, too.
While we initially focused on Server Actions for client-server data transfer, our philosophy for React is to provide the same programming model across all platforms and environments. When possible, if we introduce a feature on the client, we aim to make it also work on the server, and vice versa. This philosophy allows us to create a single set of APIs that work no matter where your app runs, making it easier to upgrade to different environments later.
\\nActions are now available in the Canary channel and will ship in the next release of React.
\\nWe introduced React Canaries as an option to adopt individual new stable features as soon as their design is close to final, before they’re released in a stable semver version.
\\nCanaries are a change to the way we develop React. Previously, features would be researched and built privately inside of Meta, so users would only see the final polished product when released to Stable. With Canaries, we’re building in public with the help of the community to finalize features we share in the React Labs blog series. This means you hear about new features sooner, as they’re being finalized instead of after they’re complete.
\\nReact Server Components, Asset Loading, Document Metadata, and Actions have all landed in the React Canary, and we’ve added docs for these features on react.dev:
\\nDirectives: \\"use client\\"
and \\"use server\\"
are bundler features designed for full-stack React frameworks. They mark the “split points” between the two environments: \\"use client\\"
instructs the bundler to generate a <script>
tag (like Astro Islands), while \\"use server\\"
tells the bundler to generate a POST endpoint (like tRPC Mutations). Together, they let you write reusable components that compose client-side interactivity with the related server-side logic.
Document Metadata: we added built-in support for rendering <title>
, <meta>
, and metadata <link>
tags anywhere in your component tree. These work the same way in all environments, including fully client-side code, SSR, and RSC. This provides built-in support for features pioneered by libraries like React Helmet.
Asset Loading: we integrated Suspense with the loading lifecycle of resources such as stylesheets, fonts, and scripts so that React takes them into account to determine whether the content in elements like <style>
, <link>
, and <script>
are ready to be displayed. We’ve also added new Resource Loading APIs like preload
and preinit
to provide greater control for when a resource should load and initialize.
Actions: As shared above, we’ve added Actions to manage sending data from the client to the server. You can add action
to elements like <form/>
, access the status with useFormStatus
, handle the result with useActionState
, and optimistically update the UI with useOptimistic
.
Since all of these features work together, it’s difficult to release them in the Stable channel individually. Releasing Actions without the complementary hooks for accessing form states would limit the practical usability of Actions. Introducing React Server Components without integrating Server Actions would complicate modifying data on the server.
\\nBefore we can release a set of features to the Stable channel, we need to ensure they work cohesively and developers have everything they need to use them in production. React Canaries allow us to develop these features individually, and release the stable APIs incrementally until the entire feature set is complete.
\\nThe current set of features in React Canary are complete and ready to release.
\\nAfter a couple of years of iteration, react@canary
is now ready to ship to react@latest
. The new features mentioned above are compatible with any environment your app runs in, providing everything needed for production use. Since Asset Loading and Document Metadata may be a breaking change for some apps, the next version of React will be a major version: React 19.
There’s still more to be done to prepare for release. In React 19, we’re also adding long-requested improvements which require breaking changes like support for Web Components. Our focus now is to land these changes, prepare for release, finalize docs for new features, and publish announcements for what’s included.
\\nWe’ll share more information about everything React 19 includes, how to adopt the new client features, and how to build support for React Server Components in the coming months.
\\nSince our last update, we’ve renamed a capability we’re researching from “Offscreen” to “Activity”. The name “Offscreen” implied that it only applied to parts of the app that were not visible, but while researching the feature we realized that it’s possible for parts of the app to be visible and inactive, such as content behind a modal. The new name more closely reflects the behavior of marking certain parts of the app “active” or “inactive”.
\\nActivity is still under research and our remaining work is to finalize the primitives that are exposed to library developers. We’ve deprioritized this area while we focus on shipping features that are more complete.
\\nIn addition to this update, our team has presented at conferences and made appearances on podcasts to speak more on our work and answer questions.
\\nSathya Gunasekaran spoke about the React Compiler at the React India conference
\\nDan Abramov gave a talk at RemixConf titled “React from Another Dimension” which explores an alternative history of how React Server Components and Actions could have been created
\\nDan Abramov was interviewed on the Changelog’s JS Party podcast about React Server Components
\\nMatt Carroll was interviewed on the Front-End Fire podcast where he discussed The Two Reacts
\\nThanks Lauren Tan, Sophie Alpert, Jason Bonta, Eli White, and Sathya Gunasekaran for reviewing this post.
\\nThanks for reading, and see you at React Conf!
May 3, 2023 by Dan Abramov, Sophie Alpert, Rick Hanlon, Sebastian Markbåge, and Andrew Clark
\\nWe’d like to offer the React community an option to adopt individual new features as soon as their design is close to final, before they’re released in a stable version—similar to how Meta has long used bleeding-edge versions of React internally. We are introducing a new officially supported Canary release channel. It lets curated setups like frameworks decouple adoption of individual React features from the React release schedule.
Typically, every React feature has gone through the same stages:
\\nexperimental_
or unstable_
. The feature is only available in the experimental
release channel. At this point, the feature is expected to change significantly.main
branch by default, which most Meta products use. At this point, any team at Meta can use this feature.This playbook works well for most features we’ve released so far. However, there can be a significant gap between when the feature is generally ready to use (step 3) and when it is released in open source (step 5).
\\nWe’d like to offer the React community an option to follow the same approach as Meta, and adopt individual new features earlier (as they become available) without having to wait for the next release cycle of React.
\\nAs always, all React features will eventually make it into a Stable release.
\\nGenerally, we do use minor releases for introducing new features.
\\nHowever, this isn’t always possible. Sometimes, new features are interconnected with other new features which have not yet been fully completed and that we’re still actively iterating on. We can’t release them separately because their implementations are related. We can’t version them separately because they affect the same packages (for example, react
and react-dom
). And we need to keep the ability to iterate on the pieces that aren’t ready without a flurry of major version releases, which semver would require us to do.
At Meta, we’ve solved this problem by building React from the main
branch, and manually updating it to a specific pinned commit every week. This is also the approach that React Native releases have been following for the last several years. Every stable release of React Native is pinned to a specific commit from the main
branch of the React repository. This lets React Native include important bugfixes and incrementally adopt new React features at the framework level without getting coupled to the global React release schedule.
We would like to make this workflow available to other frameworks and curated setups. For example, it lets a framework on top of React include a React-related breaking change before this breaking change gets included into a stable React release. This is particularly useful because some breaking changes only affect framework integrations. This lets a framework release such a change in its own minor version without breaking semver.
\\nRolling releases with the Canaries channel will allow us to have a tighter feedback loop and ensure that new features get comprehensive testing in the community. This workflow is closer to how TC39, the JavaScript standards committee, handles changes in numbered stages. New React features may be available in frameworks built on React before they are in a React stable release, just as new JavaScript features ship in browsers before they are officially ratified as part of the specification.
\\nAlthough you can technically use Experimental releases, we recommend against using them in production because experimental APIs can undergo significant breaking changes on their way to stabilization (or can even be removed entirely). While Canaries can also contain mistakes (as with any release), going forward we plan to announce any significant breaking changes in Canaries on our blog. Canaries are the closest to the code Meta runs internally, so you can generally expect them to be relatively stable. However, you do need to keep the version pinned and manually scan the GitHub commit log when updating between the pinned commits.
\\nWe expect that most people using React outside a curated setup (like a framework) will want to continue using the Stable releases. However, if you’re building a framework, you might want to consider bundling a Canary version of React pinned to a particular commit, and update it at your own pace. The benefit of that is that it lets you ship individual completed React features and bugfixes earlier for your users and at your own release schedule, similar to how React Native has been doing it for the last few years. The downside is that you would take on additional responsibility to review which React commits are being pulled in and communicate to your users which React changes are included with your releases.
\\nIf you’re a framework author and want to try this approach, please get in touch with us.
\\nCanary releases represent our best guess of what will go into the next stable React release at any given time.
\\nTraditionally, we’ve only announced breaking changes at the end of the release cycle (when doing a major release). Now that Canary releases are an officially supported way to consume React, we plan to shift towards announcing breaking changes and significant new features as they land in Canaries. For example, if we merge a breaking change that will go out in a Canary, we will write a post about it on the React blog, including codemods and migration instructions if necessary. Then, if you’re a framework author cutting a major release that updates the pinned React canary to include that change, you can link to our blog post from your release notes. Finally, when a stable major version of React is ready, we will link to those already published blog posts, which we hope will help our team make progress faster.
\\nWe plan to document APIs as they land in Canaries—even if these APIs are not yet available outside of them. APIs that are only available in Canaries will be marked with a special note on the corresponding pages. This will include APIs like use
, and some others (like cache
and createServerContext
) which we’ll send RFCs for.
If you decide to adopt the Canary workflow for your app or framework, make sure you always pin the exact version of the Canary you’re using. Since Canaries are pre-releases, they may still include breaking changes.
\\nAs we announced in March, the React Server Components conventions have been finalized, and we do not expect significant breaking changes related to their user-facing API contract. However, we can’t release support for React Server Components in a stable version of React yet because we are still working on several intertwined framework-only features (such as asset loading) and expect more breaking changes there.
\\nThis means that React Server Components are ready to be adopted by frameworks. However, until the next major React release, the only way for a framework to adopt them is to ship a pinned Canary version of React. (To avoid bundling two copies of React, frameworks that wish to do this would need to enforce resolution of react
and react-dom
to the pinned Canary they ship with their framework, and explain that to their users. As an example, this is what Next.js App Router does.)
We do not expect library authors to test every single Canary release since it would be prohibitively difficult. However, just as when we originally introduced the different React pre-release channels three years ago, we encourage libraries to run tests against both the latest Stable and latest Canary versions. If you see a change in behavior that wasn’t announced, please file a bug in the React repository so that we can help diagnose it. We expect that as this practice becomes widely adopted, it will reduce the amount of effort necessary to upgrade libraries to new major versions of React, since accidental regressions would be found as they land.
\\nStrictly speaking, Canary is not a new release channel—it used to be called Next. However, we’ve decided to rename it to avoid confusion with Next.js. We’re announcing it as a new release channel to communicate the new expectations, such as Canaries being an officially supported way to use React.
We are not introducing any changes to stable React releases.
March 22, 2023 by Joseph Savona, Josh Story, Lauren Tan, Mengdi Chen, Samuel Susla, Sathya Gunasekaran, Sebastian Markbåge, and Andrew Clark
\\nIn React Labs posts, we write about projects in active research and development. We’ve made significant progress on them since our last update, and we’d like to share what we learned.
React Server Components (or RSC) is a new application architecture designed by the React team.
\\nWe’ve first shared our research on RSC in an introductory talk and an RFC. To recap them, we are introducing a new kind of component—Server Components—that run ahead of time and are excluded from your JavaScript bundle. Server Components can run during the build, letting you read from the filesystem or fetch static content. They can also run on the server, letting you access your data layer without having to build an API. You can pass data by props from Server Components to the interactive Client Components in the browser.
\\nRSC combines the simple “request/response” mental model of server-centric Multi-Page Apps with the seamless interactivity of client-centric Single-Page Apps, giving you the best of both worlds.
\\nSince our last update, we have merged the React Server Components RFC to ratify the proposal. We resolved outstanding issues with the React Server Module Conventions proposal, and reached consensus with our partners to go with the \\"use client\\"
convention. These documents also act as specification for what an RSC-compatible implementation should support.
The biggest change is that we introduced async
/ await
as the primary way to do data fetching from Server Components. We also plan to support data loading from the client by introducing a new Hook called use
that unwraps Promises. Although we can’t support async / await
in arbitrary components in client-only apps, we plan to add support for it when you structure your client-only app similar to how RSC apps are structured.
Now that we have data fetching pretty well sorted, we’re exploring the other direction: sending data from the client to the server, so that you can execute database mutations and implement forms. We’re doing this by letting you pass Server Action functions across the server/client boundary, which the client can then call, providing seamless RPC. Server Actions also give you progressively enhanced forms before JavaScript loads.
\\nReact Server Components has shipped in Next.js App Router. This showcases a deep integration of a router that really buys into RSC as a primitive, but it’s not the only way to build a RSC-compatible router and framework. There’s a clear separation for features provided by the RSC spec and implementation. React Server Components is meant as a spec for components that work across compatible React frameworks.
\\nWe generally recommend using an existing framework, but if you need to build your own custom framework, it is possible. Building your own RSC-compatible framework is not as easy as we’d like it to be, mainly due to the deep bundler integration needed. The current generation of bundlers are great for use on the client, but they weren’t designed with first-class support for splitting a single module graph between the server and the client. This is why we’re now partnering directly with bundler developers to get the primitives for RSC built-in.
\\nSuspense lets you specify what to display on the screen while the data or code for your components is still being loaded. This lets your users progressively see more content while the page is loading as well as during the router navigations that load more data and code. However, from the user’s perspective, data loading and rendering do not tell the whole story when considering whether new content is ready. By default, browsers load stylesheets, fonts, and images independently, which can lead to UI jumps and consecutive layout shifts.
\\nWe’re working to fully integrate Suspense with the loading lifecycle of stylesheets, fonts, and images, so that React takes them into account to determine whether the content is ready to be displayed. Without any change to the way you author your React components, updates will behave in a more coherent and pleasing manner. As an optimization, we will also provide a manual way to preload assets like fonts directly from components.
\\nWe are currently implementing these features and will have more to share soon.
\\nDifferent pages and screens in your app may have different metadata like the <title>
tag, description, and other <meta>
tags specific to this screen. From the maintenance perspective, it’s more scalable to keep this information close to the React component for that page or screen. However, the HTML tags for this metadata need to be in the document <head>
which is typically rendered in a component at the very root of your app.
Today, people solve this problem with one of the two techniques.
\\nOne technique is to render a special third-party component that moves <title>
, <meta>
, and other tags inside it into the document <head>
. This works for major browsers but there are many clients which do not run client-side JavaScript, such as Open Graph parsers, and so this technique is not universally suitable.
Another technique is to server-render the page in two parts. First, the main content is rendered and all such tags are collected. Then, the <head>
is rendered with these tags. Finally, the <head>
and the main content are sent to the browser. This approach works, but it prevents you from taking advantage of the React 18’s Streaming Server Renderer because you’d have to wait for all content to render before sending the <head>
.
This is why we’re adding built-in support for rendering <title>
, <meta>
, and metadata <link>
tags anywhere in your component tree out of the box. It would work the same way in all environments, including fully client-side code, SSR, and in the future, RSC. We will share more details about this soon.
Since our previous update we’ve been actively iterating on the design of React Forget, an optimizing compiler for React. We’ve previously talked about it as an “auto-memoizing compiler”, and that is true in some sense. But building the compiler has helped us understand React’s programming model even more deeply. A better way to understand React Forget is as an automatic reactivity compiler.
\\nThe core idea of React is that developers define their UI as a function of the current state. You work with plain JavaScript values — numbers, strings, arrays, objects — and use standard JavaScript idioms — if/else, for, etc — to describe your component logic. The mental model is that React will re-render whenever the application state changes. We believe this simple mental model and keeping close to JavaScript semantics is an important principle in React’s programming model.
\\nThe catch is that React can sometimes be too reactive: it can re-render too much. For example, in JavaScript we don’t have cheap ways to compare if two objects or arrays are equivalent (having the same keys and values), so creating a new object or array on each render may cause React to do more work than it strictly needs to. This means developers have to explicitly memoize components so as to not over-react to changes.
\\nOur goal with React Forget is to ensure that React apps have just the right amount of reactivity by default: that apps re-render only when state values meaningfully change. From an implementation perspective this means automatically memoizing, but we believe that the reactivity framing is a better way to understand React and Forget. One way to think about this is that React currently re-renders when object identity changes. With Forget, React re-renders when the semantic value changes — but without incurring the runtime cost of deep comparisons.
\\nIn terms of concrete progress, since our last update we have substantially iterated on the design of the compiler to align with this automatic reactivity approach and to incorporate feedback from using the compiler internally. After some significant refactors to the compiler starting late last year, we’ve now begun using the compiler in production in limited areas at Meta. We plan to open-source it once we’ve proved it in production.
\\nFinally, a lot of people have expressed interest in how the compiler works. We’re looking forward to sharing a lot more details when we prove the compiler and open-source it. But there are a few bits we can share now:
\\nThe core of the compiler is almost completely decoupled from Babel, and the core compiler API is (roughly) old AST in, new AST out (while retaining source location data). Under the hood we use a custom code representation and transformation pipeline in order to do low-level semantic analysis. However, the primary public interface to the compiler will be via Babel and other build system plugins. For ease of testing we currently have a Babel plugin which is a very thin wrapper that calls the compiler to generate a new version of each function and swap it in.
\\nAs we refactored the compiler over the last few months, we wanted to focus on refining the core compilation model to ensure we could handle complexities such as conditionals, loops, reassignment, and mutation. However, JavaScript has a lot of ways to express each of those features: if/else, ternaries, for, for-in, for-of, etc. Trying to support the full language up-front would have delayed the point where we could validate the core model. Instead, we started with a small but representative subset of the language: let/const, if/else, for loops, objects, arrays, primitives, function calls, and a few other features. As we gained confidence in the core model and refined our internal abstractions, we expanded the supported language subset. We’re also explicit about syntax we don’t yet support, logging diagnostics and skipping compilation for unsupported input. We have utilities to try the compiler on Meta’s codebases and see what unsupported features are most common so we can prioritize those next. We’ll continue incrementally expanding towards supporting the whole language.
\\nMaking plain JavaScript in React components reactive requires a compiler with a deep understanding of semantics so that it can understand exactly what the code is doing. By taking this approach, we’re creating a system for reactivity within JavaScript that lets you write product code of any complexity with the full expressivity of the language, instead of being limited to a domain specific language.
\\nOffscreen rendering is an upcoming capability in React for rendering screens in the background without additional performance overhead. You can think of it as a version of the content-visibility
CSS property that works not only for DOM elements but React components, too. During our research, we’ve discovered a variety of use cases:
Most React developers will not interact with React’s offscreen APIs directly. Instead, offscreen rendering will be integrated into things like routers and UI libraries, and then developers who use those libraries will automatically benefit without additional work.
\\nThe idea is that you should be able to render any React tree offscreen without changing the way you write your components. When a component is rendered offscreen, it does not actually mount until the component becomes visible — its effects are not fired. For example, if a component uses useEffect
to log analytics when it appears for the first time, prerendering won’t mess up the accuracy of those analytics. Similarly, when a component goes offscreen, its effects are unmounted, too. A key feature of offscreen rendering is that you can toggle the visibility of a component without losing its state.
Since our last update, we’ve tested an experimental version of prerendering internally at Meta in our React Native apps on Android and iOS, with positive performance results. We’ve also improved how offscreen rendering works with Suspense — suspending inside an offscreen tree will not trigger Suspense fallbacks. Our remaining work involves finalizing the primitives that are exposed to library developers. We expect to publish an RFC later this year, alongside an experimental API for testing and feedback.
\\nThe Transition Tracing API lets you detect when React Transitions become slower and investigate why they may be slow. Following our last update, we have completed the initial design of the API and published an RFC. The basic capabilities have also been implemented. The project is currently on hold. We welcome feedback on the RFC and look forward to resuming its development to provide a better performance measurement tool for React. This will be particularly useful with routers built on top of React Transitions, like the Next.js App Router.
\\nIn addition to this update, our team has made recent guest appearances on community podcasts and livestreams to speak more on our work and answer questions.
\\nThanks to Andrew Clark, Dan Abramov, Dave McCabe, Luna Wei, Matt Carroll, Sean Keegan, Sebastian Silbermann, Seth Webster, and Sophie Alpert for reviewing this post.
\\nThanks for reading, and see you in the next update!
March 16, 2023 by Dan Abramov and Rachel Nabors
\\nToday we are thrilled to launch react.dev, the new home for React and its documentation. In this post, we would like to give you a tour of the new site.
First, a little bit of housekeeping.
\\nTo celebrate the launch of the new docs and, more importantly, to clearly separate the old and the new content, we’ve moved to the shorter react.dev domain. The old reactjs.org domain will now redirect here.
\\nThe old React docs are now archived at legacy.reactjs.org. All existing links to the old content will automatically redirect there to avoid “breaking the web”, but the legacy site will not get many more updates.
\\nBelieve it or not, React will soon be ten years old. In JavaScript years, it’s like a whole century! We’ve refreshed the React homepage to reflect why we think React is a great way to create user interfaces today, and updated the getting started guides to more prominently mention modern React-based frameworks.
\\nIf you haven’t seen the new homepage yet, check it out!
\\nWhen we released React Hooks in 2018, the Hooks docs assumed the reader is familiar with class components. This helped the community adopt Hooks very swiftly, but after a while the old docs failed to serve the new readers. New readers had to learn React twice: once with class components and then once again with Hooks.
\\nThe new docs teach React with Hooks from the beginning. The docs are divided in two main sections:
\\nLet’s have a closer look at what you can find in each section.
\\nThere are still a few rare class component use cases that do not yet have a Hook-based equivalent. Class components remain supported, and are documented in the Legacy API section of the new site.
The Learn section begins with the Quick Start page. It is a short introductory tour of React. It introduces the syntax for concepts like components, props, and state, but doesn’t go into much detail on how to use them.
\\nIf you like to learn by doing, we recommend checking out the Tic-Tac-Toe Tutorial next. It walks you through building a little game with React, while teaching the skills you’ll use every day. Here’s what you’ll build:
\\nimport { useState } from \'react\';\\n\\nfunction Square({ value, onSquareClick }) {\\n return (\\n <button className=\\"square\\" onClick={onSquareClick}>\\n {value}\\n </button>\\n );\\n}\\n\\nfunction Board({ xIsNext, squares, onPlay }) {\\n function handleClick(i) {\\n if (calculateWinner(squares) || squares[i]) {\\n return;\\n }\\n const nextSquares = squares.slice();\\n if (xIsNext) {\\n nextSquares[i] = \'X\';\\n } else {\\n nextSquares[i] = \'O\';\\n }\\n onPlay(nextSquares);\\n }\\n\\n const winner = calculateWinner(squares);\\n let status;\\n if (winner) {\\n status = \'Winner: \' + winner;\\n } else {\\n status = \'Next player: \' + (xIsNext ? \'X\' : \'O\');\\n }\\n\\n return (\\n <>\\n <div className=\\"status\\">{status}</div>\\n <div className=\\"board-row\\">\\n <Square value={squares[0]} onSquareClick={() => handleClick(0)} />\\n <Square value={squares[1]} onSquareClick={() => handleClick(1)} />\\n <Square value={squares[2]} onSquareClick={() => handleClick(2)} />\\n </div>\\n <div className=\\"board-row\\">\\n <Square value={squares[3]} onSquareClick={() => handleClick(3)} />\\n <Square value={squares[4]} onSquareClick={() => handleClick(4)} />\\n <Square value={squares[5]} onSquareClick={() => handleClick(5)} />\\n </div>\\n <div className=\\"board-row\\">\\n <Square value={squares[6]} onSquareClick={() => handleClick(6)} />\\n <Square value={squares[7]} onSquareClick={() => handleClick(7)} />\\n <Square value={squares[8]} onSquareClick={() => handleClick(8)} />\\n </div>\\n </>\\n );\\n}\\n\\nexport default function Game() {\\n const [history, setHistory] = useState([Array(9).fill(null)]);\\n const [currentMove, setCurrentMove] = useState(0);\\n const xIsNext = currentMove % 2 === 0;\\n const currentSquares = history[currentMove];\\n\\n function handlePlay(nextSquares) {\\n const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];\\n setHistory(nextHistory);\\n setCurrentMove(nextHistory.length - 1);\\n }\\n\\n function jumpTo(nextMove) {\\n setCurrentMove(nextMove);\\n }\\n\\n const moves = history.map((squares, move) => {\\n let description;\\n if (move > 0) {\\n description = \'Go to move #\' + move;\\n } else {\\n description = \'Go to game start\';\\n }\\n return (\\n <li key={move}>\\n <button onClick={() => jumpTo(move)}>{description}</button>\\n </li>\\n );\\n });\\n\\n return (\\n <div className=\\"game\\">\\n <div className=\\"game-board\\">\\n <Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />\\n </div>\\n <div className=\\"game-info\\">\\n <ol>{moves}</ol>\\n </div>\\n </div>\\n );\\n}\\n\\nfunction calculateWinner(squares) {\\n const lines = [\\n [0, 1, 2],\\n [3, 4, 5],\\n [6, 7, 8],\\n [0, 3, 6],\\n [1, 4, 7],\\n [2, 5, 8],\\n [0, 4, 8],\\n [2, 4, 6],\\n ];\\n for (let i = 0; i < lines.length; i++) {\\n const [a, b, c] = lines[i];\\n if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {\\n return squares[a];\\n }\\n }\\n return null;\\n}\\n\\n
We’d also like to highlight Thinking in React—that’s the tutorial that made React “click” for many of us. We’ve updated both of these classic tutorials to use function components and Hooks, so they’re as good as new.
\\nThe example above is a sandbox. We’ve added a lot of sandboxes—over 600!—everywhere throughout the site. You can edit any sandbox, or press “Fork” in the upper right corner to open it in a separate tab. Sandboxes let you quickly play with the React APIs, explore your ideas, and check your understanding.
We’d like everyone in the world to have an equal opportunity to learn React for free on their own.
\\nThis is why the Learn section is organized like a self-paced course split into chapters. The first two chapters describe the fundamentals of React. If you’re new to React, or want to refresh it in your memory, start here:
\\nThe next two chapters are more advanced, and will give you a deeper insight into the trickier parts:
\\nEvery chapter consists of several related pages. Most of these pages teach a specific skill or a technique—for example, Writing Markup with JSX, Updating Objects in State, or Sharing State Between Components. Some of the pages focus on explaining an idea—like Render and Commit, or State as a Snapshot. And there are a few, like You Might Not Need an Effect, that share our suggestions based on what we’ve learned over these years.
\\nYou don’t have to read these chapters as a sequence. Who has the time for this?! But you could. Pages in the Learn section only rely on concepts introduced by the earlier pages. If you want to read it like a book, go for it!
\\nMost pages in the Learn section end with a few challenges to check your understanding. For example, here are a few challenges from the page about Conditional Rendering.
\\nYou don’t have to solve them right now! Unless you really want to.
\\n? :
2. Show the item importance with &&
? :
Use the conditional operator (cond ? a : b
) to render a ❌ if isPacked
isn’t true
.
function Item({ name, isPacked }) {\\n return (\\n <li className=\\"item\\">\\n {name} {isPacked && \'✅\'}\\n </li>\\n );\\n}\\n\\nexport default function PackingList() {\\n return (\\n <section>\\n <h1>Sally Ride\'s Packing List</h1>\\n <ul>\\n <Item \\n isPacked={true} \\n name=\\"Space suit\\" \\n />\\n <Item \\n isPacked={true} \\n name=\\"Helmet with a golden leaf\\" \\n />\\n <Item \\n isPacked={false} \\n name=\\"Photo of Tam\\" \\n />\\n </ul>\\n </section>\\n );\\n}\\n\\n
Notice the “Show solution” button in the left bottom corner. It’s handy if you want to check yourself!
\\nWhen we couldn’t figure out how to explain something with code and words alone, we’ve added diagrams that help provide some intuition. For example, here is one of the diagrams from Preserving and Resetting State:
\\nWhen section
changes to div
, the section
is deleted and the new div
is added
You’ll also see some illustrations throughout the docs—here’s one of the browser painting the screen:
\\nIllustrated by Rachel Lee Nabors
We’ve confirmed with the browser vendors that this depiction is 100% scientifically accurate.
\\nIn the API Reference, every React API now has a dedicated page. This includes all kinds of APIs:
\\nuseState
.<Suspense>
.<input>
.renderToPipeableStream
.memo
.You’ll notice that every API page is split into at least two segments: Reference and Usage.
\\nReference describes the formal API signature by listing its arguments and return values. It’s concise, but it can feel a bit abstract if you’re not familiar with that API. It describes what an API does, but not how to use it.
\\nUsage shows why and how you would use this API in practice, like a colleague or a friend might explain. It shows the canonical scenarios for how each API was meant to be used by the React team. We’ve added color-coded snippets, examples of using different APIs together, and recipes that you can copy and paste from:
\\nIn this example, the count
state variable holds a number. Clicking the button increments it.
import { useState } from \'react\';\\n\\nexport default function Counter() {\\n const [count, setCount] = useState(0);\\n\\n function handleClick() {\\n setCount(count + 1);\\n }\\n\\n return (\\n <button onClick={handleClick}>\\n You pressed me {count} times\\n </button>\\n );\\n}\\n\\n
Some API pages also include Troubleshooting (for common problems) and Alternatives (for deprecated APIs).
\\nWe hope that this approach will make the API reference useful not only as a way to look up an argument, but as a way to see all the different things you can do with any given API—and how it connects to the other ones.
\\nThat’s a wrap for our little tour! Have a look around the new website, see what you like or don’t like, and keep the feedback coming in our issue tracker.
\\nWe acknowledge this project has taken a long time to ship. We wanted to maintain a high quality bar that the React community deserves. While writing these docs and creating all of the examples, we found mistakes in some of our own explanations, bugs in React, and even gaps in the React design that we are now working to address. We hope that the new documentation will help us hold React itself to a higher bar in the future.
\\nWe’ve heard many of your requests to expand the content and functionality of the website, for example:
\\nNow that react.dev is out, we will be able to shift our focus from “catching up” with the third-party React educational resources to adding new information and further improving our new website.
\\nWe think there’s never been a better time to learn React.
\\nOn the React team, Rachel Nabors led the project (and provided the illustrations), and Dan Abramov designed the curriculum. They co-authored most of the content together as well.
\\nOf course, no project this large happens in isolation. We have a lot of people to thank!
\\nSylwia Vargas overhauled our examples to go beyond “foo/bar/baz” and kittens, and feature scientists, artists and cities from around the world. Maggie Appleton turned our doodles into a clear diagram system.
\\nThanks to David McCabe, Sophie Alpert, Rick Hanlon, Andrew Clark, and Matt Carroll for additional writing contributions. We’d also like to thank Natalia Tepluhina and Sebastian Markbåge for their ideas and feedback.
\\nThanks to Dan Lebowitz for the site design and Razvan Gradinar for the sandbox design.
\\nOn the development front, thanks to Jared Palmer for prototype development. Thanks to Dane Grant and Dustin Goodman from ThisDotLabs for their support on UI development. Thanks to Ives van Hoorne, Alex Moldovan, Jasper De Moor, and Danilo Woznica from CodeSandbox for their work with sandbox integration. Thanks to Rick Hanlon for spot development and design work, finessing our colors and finer details. Thanks to Harish Kumar and Luna Ruan for adding new features to the site and helping maintain it.
\\nHuge thanks to the folks who volunteered their time to participate in the alpha and beta testing program. Your enthusiasm and invaluable feedback helped us shape these docs. A special shout out to our beta tester, Debbie O’Brien, who gave a talk about her experience using the React docs at React Conf 2021.
\\nFinally, thanks to the React community for being the inspiration behind this effort. You are the reason we do this, and we hope that the new docs will help you use React to build any user interface that you want.
June 15, 2022 by Andrew Clark, Dan Abramov, Jan Kassens, Joseph Savona, Josh Story, Lauren Tan, Luna Ruan, Mengdi Chen, Rick Hanlon, Robert Zhang, Sathya Gunasekaran, Sebastian Markbåge, and Xuan Huang
\\nReact 18 was years in the making, and with it brought valuable lessons for the React team. Its release was the result of many years of research and exploring many paths. Some of those paths were successful; many more were dead-ends that led to new insights. One lesson we’ve learned is that it’s frustrating for the community to wait for new features without having insight into these paths that we’re exploring.
We typically have a number of projects being worked on at any time, ranging from the more experimental to the clearly defined. Looking ahead, we’d like to start regularly sharing more about what we’ve been working on with the community across these projects.
\\nTo set expectations, this is not a roadmap with clear timelines. Many of these projects are under active research and are difficult to put concrete ship dates on. They may possibly never even ship in their current iteration depending on what we learn. Instead, we want to share with you the problem spaces we’re actively thinking about, and what we’ve learned so far.
\\nWe announced an experimental demo of React Server Components (RSC) in December 2020. Since then we’ve been finishing up its dependencies in React 18, and working on changes inspired by experimental feedback.
\\nIn particular, we’re abandoning the idea of having forked I/O libraries (eg react-fetch), and instead adopting an async/await model for better compatibility. This doesn’t technically block RSC’s release because you can also use routers for data fetching. Another change is that we’re also moving away from the file extension approach in favor of annotating boundaries.
\\nWe’re working together with Vercel and Shopify to unify bundler support for shared semantics in both Webpack and Vite. Before launch, we want to make sure that the semantics of RSCs are the same across the whole React ecosystem. This is the major blocker for reaching stable.
\\nCurrently, assets like scripts, external styles, fonts, and images are typically preloaded and loaded using external systems. This can make it tricky to coordinate across new environments like streaming, Server Components, and more.\\nWe’re looking at adding APIs to preload and load deduplicated external assets through React APIs that work in all React environments.
\\nWe’re also looking at having these support Suspense so you can have images, CSS, and fonts that block display until they’re loaded but don’t block streaming and concurrent rendering. This can help avoid “popcorning“ as the visuals pop and layout shifts.
\\nStatic Site Generation (SSG) and Incremental Static Regeneration (ISR) are great ways to get performance for cacheable pages, but we think we can add features to improve performance of dynamic Server Side Rendering (SSR) – especially when most but not all of the content is cacheable. We’re exploring ways to optimize server rendering utilizing compilation and static passes.
\\nWe gave an early preview of React Forget at React Conf 2021. It’s a compiler that automatically generates the equivalent of useMemo
and useCallback
calls to minimize the cost of re-rendering, while retaining React’s programming model.
Recently, we finished a rewrite of the compiler to make it more reliable and capable. This new architecture allows us to analyze and memoize more complex patterns such as the use of local mutations, and opens up many new compile-time optimization opportunities beyond just being on par with memoization Hooks.
\\nWe’re also working on a playground for exploring many aspects of the compiler. While the goal of the playground is to make development of the compiler easier, we think that it will make it easier to try it out and build intuition for what the compiler does. It reveals various insights into how it works under the hood, and live renders the compiler’s outputs as you type. This will be shipped together with the compiler when it’s released.
\\nToday, if you want to hide and show a component, you have two options. One is to add or remove it from the tree completely. The problem with this approach is that the state of your UI is lost each time you unmount, including state stored in the DOM, like scroll position.
\\nThe other option is to keep the component mounted and toggle the appearance visually using CSS. This preserves the state of your UI, but it comes at a performance cost, because React must keep rendering the hidden component and all of its children whenever it receives new updates.
\\nOffscreen introduces a third option: hide the UI visually, but deprioritize its content. The idea is similar in spirit to the content-visibility
CSS property: when content is hidden, it doesn’t need to stay in sync with the rest of the UI. React can defer the rendering work until the rest of the app is idle, or until the content becomes visible again.
Offscreen is a low level capability that unlocks high level features. Similar to React’s other concurrent features like startTransition
, in most cases you won’t interact with the Offscreen API directly, but instead via an opinionated framework to implement patterns like:
Currently, React has two profiling tools. The original Profiler shows an overview of all the commits in a profiling session. For each commit, it also shows all components that rendered and the amount of time it took for them to render. We also have a beta version of a Timeline Profiler introduced in React 18 that shows when components schedule updates and when React works on these updates. Both of these profilers help developers identify performance problems in their code.
\\nWe’ve realized that developers don’t find knowing about individual slow commits or components out of context that useful. It’s more useful to know about what actually causes the slow commits. And that developers want to be able to track specific interactions (eg a button click, an initial load, or a page navigation) to watch for performance regressions and to understand why an interaction was slow and how to fix it.
\\nWe previously tried to solve this issue by creating an Interaction Tracing API, but it had some fundamental design flaws that reduced the accuracy of tracking why an interaction was slow and sometimes resulted in interactions never ending. We ended up removing this API because of these issues.
\\nWe are working on a new version for the Interaction Tracing API (tentatively called Transition Tracing because it is initiated via startTransition
) that solves these problems.
Last year, we announced the beta version of the new React documentation website (later shipped as react.dev) of the new React documentation website. The new learning materials teach Hooks first and has new diagrams, illustrations, as well as many interactive examples and challenges. We took a break from that work to focus on the React 18 release, but now that React 18 is out, we’re actively working to finish and ship the new documentation.
\\nWe are currently writing a detailed section about effects, as we’ve heard that is one of the more challenging topics for both new and experienced React users. Synchronizing with Effects is the first published page in the series, and there are more to come in the following weeks. When we first started writing a detailed section about effects, we’ve realized that many common effect patterns can be simplified by adding a new primitive to React. We’ve shared some initial thoughts on that in the useEvent RFC. It is currently in early research, and we are still iterating on the idea. We appreciate the community’s comments on the RFC so far, as well as the feedback and contributions to the ongoing documentation rewrite. We’d specifically like to thank Harish Kumar for submitting and reviewing many improvements to the new website implementation.
\\nThanks to Sophie Alpert for reviewing this blog post!
March 29, 2022 by The React Team
\\nReact 18 is now available on npm! In our last post, we shared step-by-step instructions for upgrading your app to React 18. In this post, we’ll give an overview of what’s new in React 18, and what it means for the future.
Our latest major version includes out-of-the-box improvements like automatic batching, new APIs like startTransition, and streaming server-side rendering with support for Suspense.
\\nMany of the features in React 18 are built on top of our new concurrent renderer, a behind-the-scenes change that unlocks powerful new capabilities. Concurrent React is opt-in — it’s only enabled when you use a concurrent feature — but we think it will have a big impact on the way people build applications.
\\nWe’ve spent years researching and developing support for concurrency in React, and we’ve taken extra care to provide a gradual adoption path for existing users. Last summer, we formed the React 18 Working Group to gather feedback from experts in the community and ensure a smooth upgrade experience for the entire React ecosystem.
\\nIn case you missed it, we shared a lot of this vision at React Conf 2021:
\\nBelow is a full overview of what to expect in this release, starting with Concurrent Rendering.
\\nFor React Native users, React 18 will ship in React Native with the New React Native Architecture. For more information, see the React Conf keynote here.
The most important addition in React 18 is something we hope you never have to think about: concurrency. We think this is largely true for application developers, though the story may be a bit more complicated for library maintainers.
\\nConcurrency is not a feature, per se. It’s a new behind-the-scenes mechanism that enables React to prepare multiple versions of your UI at the same time. You can think of concurrency as an implementation detail — it’s valuable because of the features that it unlocks. React uses sophisticated techniques in its internal implementation, like priority queues and multiple buffering. But you won’t see those concepts anywhere in our public APIs.
\\nWhen we design APIs, we try to hide implementation details from developers. As a React developer, you focus on what you want the user experience to look like, and React handles how to deliver that experience. So we don’t expect React developers to know how concurrency works under the hood.
\\nHowever, Concurrent React is more important than a typical implementation detail — it’s a foundational update to React’s core rendering model. So while it’s not super important to know how concurrency works, it may be worth knowing what it is at a high level.
\\nA key property of Concurrent React is that rendering is interruptible. When you first upgrade to React 18, before adding any concurrent features, updates are rendered the same as in previous versions of React — in a single, uninterrupted, synchronous transaction. With synchronous rendering, once an update starts rendering, nothing can interrupt it until the user can see the result on screen.
\\nIn a concurrent render, this is not always the case. React may start rendering an update, pause in the middle, then continue later. It may even abandon an in-progress render altogether. React guarantees that the UI will appear consistent even if a render is interrupted. To do this, it waits to perform DOM mutations until the end, once the entire tree has been evaluated. With this capability, React can prepare new screens in the background without blocking the main thread. This means the UI can respond immediately to user input even if it’s in the middle of a large rendering task, creating a fluid user experience.
\\nAnother example is reusable state. Concurrent React can remove sections of the UI from the screen, then add them back later while reusing the previous state. For example, when a user tabs away from a screen and back, React should be able to restore the previous screen in the same state it was in before. In an upcoming minor, we’re planning to add a new component called <Offscreen>
that implements this pattern. Similarly, you’ll be able to use Offscreen to prepare new UI in the background so that it’s ready before the user reveals it.
Concurrent rendering is a powerful new tool in React and most of our new features are built to take advantage of it, including Suspense, transitions, and streaming server rendering. But React 18 is just the beginning of what we aim to build on this new foundation.
\\nTechnically, concurrent rendering is a breaking change. Because concurrent rendering is interruptible, components behave slightly differently when it is enabled.
\\nIn our testing, we’ve upgraded thousands of components to React 18. What we’ve found is that nearly all existing components “just work” with concurrent rendering, without any changes. However, some of them may require some additional migration effort. Although the changes are usually small, you’ll still have the ability to make them at your own pace. The new rendering behavior in React 18 is only enabled in the parts of your app that use new features.
\\nThe overall upgrade strategy is to get your application running on React 18 without breaking existing code. Then you can gradually start adding concurrent features at your own pace. You can use <StrictMode>
to help surface concurrency-related bugs during development. Strict Mode doesn’t affect production behavior, but during development it will log extra warnings and double-invoke functions that are expected to be idempotent. It won’t catch everything, but it’s effective at preventing the most common types of mistakes.
After you upgrade to React 18, you’ll be able to start using concurrent features immediately. For example, you can use startTransition to navigate between screens without blocking user input. Or useDeferredValue to throttle expensive re-renders.
\\nHowever, long term, we expect the main way you’ll add concurrency to your app is by using a concurrent-enabled library or framework. In most cases, you won’t interact with concurrent APIs directly. For example, instead of developers calling startTransition whenever they navigate to a new screen, router libraries will automatically wrap navigations in startTransition.
\\nIt may take some time for libraries to upgrade to be concurrent compatible. We’ve provided new APIs to make it easier for libraries to take advantage of concurrent features. In the meantime, please be patient with maintainers as we work to gradually migrate the React ecosystem.
\\nFor more info, see our previous post: How to upgrade to React 18.
\\nIn React 18, you can start using Suspense for data fetching in opinionated frameworks like Relay, Next.js, Hydrogen, or Remix. Ad hoc data fetching with Suspense is technically possible, but still not recommended as a general strategy.
\\nIn the future, we may expose additional primitives that could make it easier to access your data with Suspense, perhaps without the use of an opinionated framework. However, Suspense works best when it’s deeply integrated into your application’s architecture: your router, your data layer, and your server rendering environment. So even long term, we expect that libraries and frameworks will play a crucial role in the React ecosystem.
\\nAs in previous versions of React, you can also use Suspense for code splitting on the client with React.lazy. But our vision for Suspense has always been about much more than loading code — the goal is to extend support for Suspense so that eventually, the same declarative Suspense fallback can handle any asynchronous operation (loading code, data, images, etc).
\\nServer Components is an upcoming feature that allows developers to build apps that span the server and client, combining the rich interactivity of client-side apps with the improved performance of traditional server rendering. Server Components is not inherently coupled to Concurrent React, but it’s designed to work best with concurrent features like Suspense and streaming server rendering.
\\nServer Components is still experimental, but we expect to release an initial version in a minor 18.x release. In the meantime, we’re working with frameworks like Next.js, Hydrogen, and Remix to advance the proposal and get it ready for broad adoption.
\\nBatching is when React groups multiple state updates into a single re-render for better performance. Without automatic batching, we only batched updates inside React event handlers. Updates inside of promises, setTimeout, native event handlers, or any other event were not batched in React by default. With automatic batching, these updates will be batched automatically:
\\n// Before: only React events were batched.
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React will render twice, once for each state update (no batching)
}, 1000);
// After: updates inside of timeouts, promises,
// native event handlers or any other event are batched.
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React will only re-render once at the end (that\'s batching!)
}, 1000);
For more info, see this post for Automatic batching for fewer renders in React 18.
\\nA transition is a new concept in React to distinguish between urgent and non-urgent updates.
\\nUrgent updates like typing, clicking, or pressing, need immediate response to match our intuitions about how physical objects behave. Otherwise they feel “wrong”. However, transitions are different because the user doesn’t expect to see every intermediate value on screen.
\\nFor example, when you select a filter in a dropdown, you expect the filter button itself to respond immediately when you click. However, the actual results may transition separately. A small delay would be imperceptible and often expected. And if you change the filter again before the results are done rendering, you only care to see the latest results.
\\nTypically, for the best user experience, a single user input should result in both an urgent update and a non-urgent one. You can use startTransition API inside an input event to inform React which updates are urgent and which are “transitions”:
\\nimport { startTransition } from \'react\';
// Urgent: Show what was typed
setInputValue(input);
// Mark any state updates inside as transitions
startTransition(() => {
// Transition: Show the results
setSearchQuery(input);
});
Updates wrapped in startTransition are handled as non-urgent and will be interrupted if more urgent updates like clicks or key presses come in. If a transition gets interrupted by the user (for example, by typing multiple characters in a row), React will throw out the stale rendering work that wasn’t finished and render only the latest update.
\\nuseTransition
: a Hook to start transitions, including a value to track the pending state.startTransition
: a method to start transitions when the Hook cannot be used.Transitions will opt in to concurrent rendering, which allows the update to be interrupted. If the content re-suspends, transitions also tell React to continue showing the current content while rendering the transition content in the background (see the Suspense RFC for more info).
\\nSee docs for transitions here.
\\nSuspense lets you declaratively specify the loading state for a part of the component tree if it’s not yet ready to be displayed:
\\n<Suspense fallback={<Spinner />}>
<Comments />
</Suspense>
Suspense makes the “UI loading state” a first-class declarative concept in the React programming model. This lets us build higher-level features on top of it.
\\nWe introduced a limited version of Suspense several years ago. However, the only supported use case was code splitting with React.lazy, and it wasn’t supported at all when rendering on the server.
\\nIn React 18, we’ve added support for Suspense on the server and expanded its capabilities using concurrent rendering features.
\\nSuspense in React 18 works best when combined with the transition API. If you suspend during a transition, React will prevent already-visible content from being replaced by a fallback. Instead, React will delay the render until enough data has loaded to prevent a bad loading state.
\\nFor more, see the RFC for Suspense in React 18.
\\nIn this release we took the opportunity to redesign the APIs we expose for rendering on the client and server. These changes allow users to continue using the old APIs in React 17 mode while they upgrade to the new APIs in React 18.
\\nThese new APIs are now exported from react-dom/client
:
createRoot
: New method to create a root to render
or unmount
. Use it instead of ReactDOM.render
. New features in React 18 don’t work without it.hydrateRoot
: New method to hydrate a server rendered application. Use it instead of ReactDOM.hydrate
in conjunction with the new React DOM Server APIs. New features in React 18 don’t work without it.Both createRoot
and hydrateRoot
accept a new option called onRecoverableError
in case you want to be notified when React recovers from errors during rendering or hydration for logging. By default, React will use reportError
, or console.error
in the older browsers.
See docs for React DOM Client here.
\\nThese new APIs are now exported from react-dom/server
and have full support for streaming Suspense on the server:
renderToPipeableStream
: for streaming in Node environments.renderToReadableStream
: for modern edge runtime environments, such as Deno and Cloudflare workers.The existing renderToString
method keeps working but is discouraged.
See docs for React DOM Server here.
\\nIn the future, we’d like to add a feature that allows React to add and remove sections of the UI while preserving state. For example, when a user tabs away from a screen and back, React should be able to immediately show the previous screen. To do this, React would unmount and remount trees using the same component state as before.
\\nThis feature will give React apps better performance out-of-the-box, but requires components to be resilient to effects being mounted and destroyed multiple times. Most effects will work without any changes, but some effects assume they are only mounted or destroyed once.
\\nTo help surface these issues, React 18 introduces a new development-only check to Strict Mode. This new check will automatically unmount and remount every component, whenever a component mounts for the first time, restoring the previous state on the second mount.
\\nBefore this change, React would mount the component and create the effects:
\\n* React mounts the component.
* Layout effects are created.
* Effects are created.
With Strict Mode in React 18, React will simulate unmounting and remounting the component in development mode:
\\n* React mounts the component.
* Layout effects are created.
* Effects are created.
* React simulates unmounting the component.
* Layout effects are destroyed.
* Effects are destroyed.
* React simulates mounting the component with the previous state.
* Layout effects are created.
* Effects are created.
See docs for ensuring reusable state here.
\\nuseId
is a new Hook for generating unique IDs on both the client and server, while avoiding hydration mismatches. It is primarily useful for component libraries integrating with accessibility APIs that require unique IDs. This solves an issue that already exists in React 17 and below, but it’s even more important in React 18 because of how the new streaming server renderer delivers HTML out-of-order. See docs here.
\\n\\nNote
\\n\\n
useId
is not for generating keys in a list. Keys should be generated from your data.
useTransition
and startTransition
let you mark some state updates as not urgent. Other state updates are considered urgent by default. React will allow urgent state updates (for example, updating a text input) to interrupt non-urgent state updates (for example, rendering a list of search results). See docs here.
useDeferredValue
lets you defer re-rendering a non-urgent part of the tree. It is similar to debouncing, but has a few advantages compared to it. There is no fixed time delay, so React will attempt the deferred render right after the first render is reflected on the screen. The deferred render is interruptible and doesn’t block user input. See docs here.
useSyncExternalStore
is a new Hook that allows external stores to support concurrent reads by forcing updates to the store to be synchronous. It removes the need for useEffect when implementing subscriptions to external data sources, and is recommended for any library that integrates with state external to React. See docs here.
\\n\\nNote
\\n\\n
useSyncExternalStore
is intended to be used by libraries, not application code.
useInsertionEffect
is a new Hook that allows CSS-in-JS libraries to address performance issues of injecting styles in render. Unless you’ve already built a CSS-in-JS library we don’t expect you to ever use this. This Hook will run after the DOM is mutated, but before layout effects read the new layout. This solves an issue that already exists in React 17 and below, but is even more important in React 18 because React yields to the browser during concurrent rendering, giving it a chance to recalculate layout. See docs here.
\\n\\nNote
\\n\\n
useInsertionEffect
is intended to be used by libraries, not application code.
See How to Upgrade to React 18 for step-by-step instructions and a full list of breaking and notable changes.
\\nuseTransition
and useDeferredValue
to separate urgent updates from transitions. (#10426, #10715, #15593, #15272, #15578, #15769, #17058, #18796, #19121, #19703, #19719, #19724, #20672, #20976 by @acdlite, @lunaruan, @rickhanlonii, and @sebmarkbage)useId
for generating unique IDs. (#17322, #18576, #22644, #22672, #21260 by @acdlite, @lunaruan, and @sebmarkbage)useSyncExternalStore
to help external store libraries integrate with React. (#15022, #18000, #18771, #22211, #22292, #22239, #22347, #23150 by @acdlite, @bvaughn, and @drarmstr)startTransition
as a version of useTransition
without pending feedback. (#19696 by @rickhanlonii)useInsertionEffect
for CSS-in-JS libraries. (#21913 by @rickhanlonii)<StrictMode>
re-run effects to check for restorable state. (#19523 , #21418 by @bvaughn and @lunaruan)object-assign
polyfill. (#23351 by @sebmarkbage)unstable_changedBits
API. (#20953 by @acdlite)useEffect
resulting from discrete events like clicks synchronously. (#21150 by @acdlite)fallback={undefined}
now behaves the same as null
and isn’t ignored. (#21854 by @rickhanlonii)lazy()
resolving to the same component equivalent. (#20357 by @sebmarkbage)setImmediate
when available over MessageChannel
. (#20834 by @gaearon)useReducer
observing incorrect props by removing the eager bailout mechanism. (#22445 by @josephsavona)setState
being ignored in Safari when appending iframes. (#23111 by @gaearon)ZonedDateTime
in the tree. (#20617 by @dimaqq)null
in tests. (#22695 by @SimenB)onLoad
not triggering when concurrent features are on. (#23316 by @gnoff)NaN
. (#23333 by @hachibeeDI)null
in tests. (#22695 by @SimenB)package.json
as one of the entry points. (#22954 by @Jack)createRoot
and hydrateRoot
. (#10239, #11225, #12117, #13732, #15502, #15532, #17035, #17165, #20669, #20748, #20888, #21072, #21417, #21652, #21687, #23207, #23385 by @acdlite, @bvaughn, @gaearon, @lunaruan, @rickhanlonii, @trueadm, and @sebmarkbage)aria-description
to the list of known ARIA attributes. (#22142 by @mahyareb)onResize
event to video elements. (#21973 by @rileyjshaw)imageSizes
and imageSrcSet
to known props. (#22550 by @eps1lon)<option>
children if value
is provided. (#21431 by @sebmarkbage)aspectRatio
style not being applied. (#21100 by @gaearon)renderSubtreeIntoContainer
is called. (#23355 by @acdlite)renderToNodeStream
. (#23359 by @sebmarkbage)act
is used in production. (#21686 by @acdlite)global.IS_REACT_ACT_ENVIRONMENT
. (#22561 by @acdlite)act
batch updates. (#21797 by @acdlite)exports
field to package.json
. (#23087 by @otakustay)March 08, 2022 by Rick Hanlon
\\nAs we shared in the release post, React 18 introduces features powered by our new concurrent renderer, with a gradual adoption strategy for existing applications. In this post, we will guide you through the steps for upgrading to React 18.
Please report any issues you encounter while upgrading to React 18.
For React Native users, React 18 will ship in a future version of React Native. This is because React 18 relies on the New React Native Architecture to benefit from the new capabilities presented in this blogpost. For more information, see the React Conf keynote here.
To install the latest version of React:
\\nnpm install react react-dom
Or if you’re using yarn:
\\nyarn add react react-dom
When you first install React 18, you will see a warning in the console:
\\nReact 18 introduces a new root API which provides better ergonomics for managing roots. The new root API also enables the new concurrent renderer, which allows you to opt-into concurrent features.
\\n// Before
import { render } from \'react-dom\';
const container = document.getElementById(\'app\');
render(<App tab=\\"home\\" />, container);
// After
import { createRoot } from \'react-dom/client\';
const container = document.getElementById(\'app\');
const root = createRoot(container); // createRoot(container!) if you use TypeScript
root.render(<App tab=\\"home\\" />);
We’ve also changed unmountComponentAtNode
to root.unmount
:
// Before
unmountComponentAtNode(container);
// After
root.unmount();
We’ve also removed the callback from render, since it usually does not have the expected result when using Suspense:
\\n// Before
const container = document.getElementById(\'app\');
render(<App tab=\\"home\\" />, container, () => {
console.log(\'rendered\');
});
// After
function AppWithCallbackAfterRender() {
useEffect(() => {
console.log(\'rendered\');
});
return <App tab=\\"home\\" />
}
const container = document.getElementById(\'app\');
const root = createRoot(container);
root.render(<AppWithCallbackAfterRender />);
There is no one-to-one replacement for the old render callback API — it depends on your use case. See the working group post for Replacing render with createRoot for more information.
Finally, if your app uses server-side rendering with hydration, upgrade hydrate
to hydrateRoot
:
// Before
import { hydrate } from \'react-dom\';
const container = document.getElementById(\'app\');
hydrate(<App tab=\\"home\\" />, container);
// After
import { hydrateRoot } from \'react-dom/client\';
const container = document.getElementById(\'app\');
const root = hydrateRoot(container, <App tab=\\"home\\" />);
// Unlike with createRoot, you don\'t need a separate root.render() call here.
For more information, see the working group discussion here.
\\nIf your app doesn’t work after upgrading, check whether it’s wrapped in <StrictMode>
. Strict Mode has gotten stricter in React 18, and not all your components may be resilient to the new checks it adds in development mode. If removing Strict Mode fixes your app, you can remove it during the upgrade, and then add it back (either at the top or for a part of the tree) after you fix the issues that it’s pointing out.
In this release, we’re revamping our react-dom/server
APIs to fully support Suspense on the server and Streaming SSR. As part of these changes, we’re deprecating the old Node streaming API, which does not support incremental Suspense streaming on the server.
Using this API will now warn:
\\nrenderToNodeStream
: Deprecated ⛔️️Instead, for streaming in Node environments, use:
\\nrenderToPipeableStream
: New ✨We’re also introducing a new API to support streaming SSR with Suspense for modern edge runtime environments, such as Deno and Cloudflare workers:
\\nrenderToReadableStream
: New ✨The following APIs will continue working, but with limited support for Suspense:
\\nrenderToString
: Limited ⚠️renderToStaticMarkup
: Limited ⚠️Finally, this API will continue to work for rendering e-mails:
\\nrenderToStaticNodeStream
For more information on the changes to server rendering APIs, see the working group post on Upgrading to React 18 on the server, a deep dive on the new Suspense SSR Architecture, and Shaundai Person’s talk on Streaming Server Rendering with Suspense at React Conf 2021.
\\nIf your project uses TypeScript, you will need to update your @types/react
and @types/react-dom
dependencies to the latest versions. The new types are safer and catch issues that used to be ignored by the type checker. The most notable change is that the children
prop now needs to be listed explicitly when defining props, for example:
interface MyButtonProps {
color: string;
children?: React.ReactNode;
}
See the React 18 typings pull request for a full list of type-only changes. It links to example fixes in library types so you can see how to adjust your code. You can use the automated migration script to help port your application code to the new and safer typings faster.
\\nIf you find a bug in the typings, please file an issue in the DefinitelyTyped repo.
\\nReact 18 adds out-of-the-box performance improvements by doing more batching by default. Batching is when React groups multiple state updates into a single re-render for better performance. Before React 18, we only batched updates inside React event handlers. Updates inside of promises, setTimeout, native event handlers, or any other event were not batched in React by default:
\\n// Before React 18 only React events were batched
function handleClick() {
setCount(c => c + 1);
setFlag(f => !f);
// React will only re-render once at the end (that\'s batching!)
}
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React will render twice, once for each state update (no batching)
}, 1000);
Starting in React 18 with createRoot
, all updates will be automatically batched, no matter where they originate from. This means that updates inside of timeouts, promises, native event handlers or any other event will batch the same way as updates inside of React events:
// After React 18 updates inside of timeouts, promises,
// native event handlers or any other event are batched.
function handleClick() {
setCount(c => c + 1);
setFlag(f => !f);
// React will only re-render once at the end (that\'s batching!)
}
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React will only re-render once at the end (that\'s batching!)
}, 1000);
This is a breaking change, but we expect this to result in less work rendering, and therefore better performance in your applications. To opt-out of automatic batching, you can use flushSync
:
import { flushSync } from \'react-dom\';
function handleClick() {
flushSync(() => {
setCounter(c => c + 1);
});
// React has updated the DOM by now
flushSync(() => {
setFlag(f => !f);
});
// React has updated the DOM by now
}
For more information, see the Automatic batching deep dive.
\\nIn the React 18 Working Group we worked with library maintainers to create new APIs needed to support concurrent rendering for use cases specific to their use case in areas like styles, and external stores. To support React 18, some libraries may need to switch to one of the following APIs:
\\nuseSyncExternalStore
is a new Hook that allows external stores to support concurrent reads by forcing updates to the store to be synchronous. This new API is recommended for any library that integrates with state external to React. For more information, see the useSyncExternalStore overview post and useSyncExternalStore API details.useInsertionEffect
is a new Hook that allows CSS-in-JS libraries to address performance issues of injecting styles in render. Unless you’ve already built a CSS-in-JS library we don’t expect you to ever use this. This Hook will run after the DOM is mutated, but before layout effects read the new layout. This solves an issue that already exists in React 17 and below, but is even more important in React 18 because React yields to the browser during concurrent rendering, giving it a chance to recalculate layout. For more information, see the Library Upgrade Guide for <style>
.React 18 also introduces new APIs for concurrent rendering such as startTransition
, useDeferredValue
and useId
, which we share more about in the release post.
In the future, we’d like to add a feature that allows React to add and remove sections of the UI while preserving state. For example, when a user tabs away from a screen and back, React should be able to immediately show the previous screen. To do this, React would unmount and remount trees using the same component state as before.
\\nThis feature will give React better performance out-of-the-box, but requires components to be resilient to effects being mounted and destroyed multiple times. Most effects will work without any changes, but some effects assume they are only mounted or destroyed once.
\\nTo help surface these issues, React 18 introduces a new development-only check to Strict Mode. This new check will automatically unmount and remount every component, whenever a component mounts for the first time, restoring the previous state on the second mount.
\\nBefore this change, React would mount the component and create the effects:
\\n* React mounts the component.
* Layout effects are created.
* Effect effects are created.
With Strict Mode in React 18, React will simulate unmounting and remounting the component in development mode:
\\n* React mounts the component.
* Layout effects are created.
* Effect effects are created.
* React simulates unmounting the component.
* Layout effects are destroyed.
* Effects are destroyed.
* React simulates mounting the component with the previous state.
* Layout effect setup code runs
* Effect setup code runs
For more information, see the Working Group posts for Adding Reusable State to StrictMode and How to support Reusable State in Effects.
\\nWhen you first update your tests to use createRoot
, you may see this warning in your test console:
To fix this, set globalThis.IS_REACT_ACT_ENVIRONMENT
to true
before running your test:
// In your test setup file
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
The purpose of the flag is to tell React that it’s running in a unit test-like environment. React will log helpful warnings if you forget to wrap an update with act
.
You can also set the flag to false
to tell React that act
isn’t needed. This can be useful for end-to-end tests that simulate a full browser environment.
Eventually, we expect testing libraries will configure this for you automatically. For example, the next version of React Testing Library has built-in support for React 18 without any additional configuration.
\\nMore background on the act
testing API and related changes is available in the working group.
In this release, React is dropping support for Internet Explorer, which is going out of support on June 15, 2022. We’re making this change now because new features introduced in React 18 are built using modern browser features such as microtasks which cannot be adequately polyfilled in IE.
\\nIf you need to support Internet Explorer we recommend you stay with React 17.
\\nreact-dom
: ReactDOM.render
has been deprecated. Using it will warn and run your app in React 17 mode.react-dom
: ReactDOM.hydrate
has been deprecated. Using it will warn and run your app in React 17 mode.react-dom
: ReactDOM.unmountComponentAtNode
has been deprecated.react-dom
: ReactDOM.renderSubtreeIntoContainer
has been deprecated.react-dom/server
: ReactDOMServer.renderToNodeStream
has been deprecated.<Suspense>
boundary in the tree. This ensures the hydrated tree is consistent and avoids potential privacy and security holes that can be caused by hydration mismatches.Promise
, Symbol
, and Object.assign
. If you support older browsers and devices such as Internet Explorer which do not provide modern browser features natively or have non-compliant implementations, consider including a global polyfill in your bundled application.undefined
: React no longer warns if you return undefined
from a component. This makes the allowed component return values consistent with values that are allowed in the middle of a component tree. We suggest to use a linter to prevent mistakes like forgetting a return
statement before JSX.act
warnings are now opt-in: If you’re running end-to-end tests, the act
warnings are unnecessary. We’ve introduced an opt-in mechanism so you can enable them only for unit tests where they are useful and beneficial.setState
on unmounted components: Previously, React warned about memory leaks when you call setState
on an unmounted component. This warning was added for subscriptions, but people primarily run into it in scenarios where setting state is fine, and workarounds make the code worse. We’ve removed this warning.renderToString
: Will no longer error when suspending on the server. Instead, it will emit the fallback HTML for the closest <Suspense>
boundary and then retry rendering the same content on the client. It is still recommended that you switch to a streaming API like renderToPipeableStream
or renderToReadableStream
instead.renderToStaticMarkup
: Will no longer error when suspending on the server. Instead, it will emit the fallback HTML for the closest <Suspense>
boundary.You can view the full changelog here.
December 17, 2021 by Jesslyn Tannady and Rick Hanlon
\\nLast week we hosted our 6th React Conf. In previous years, we’ve used the React Conf stage to deliver industry changing announcements such as React Native and React Hooks. This year, we shared our multi-platform vision for React, starting with the release of React 18 and gradual adoption of concurrent features.
This was the first time React Conf was hosted online, and it was streamed for free, translated to 8 different languages. Participants from all over the world joined our conference Discord and the replay event for accessibility in all timezones. Over 50,000 people registered, with over 60,000 views of 19 talks, and 5,000 participants in Discord across both events.
\\nAll the talks are available to stream online.
\\nHere’s a summary of what was shared on stage:
\\nIn the keynote, we shared our vision for the future of React starting with React 18.
\\nReact 18 adds the long-awaited concurrent renderer and updates to Suspense without any major breaking changes. Apps can upgrade to React 18 and begin gradually adopting concurrent features with the amount of effort on par with any other major release.
\\nThis means there is no concurrent mode, only concurrent features.
\\nIn the keynote, we also shared our vision for Suspense, Server Components, new React working groups, and our long-term many-platform vision for React Native.
\\nWatch the full keynote from Andrew Clark, Juan Tejada, Lauren Tan, and Rick Hanlon here:
\\n\\nIn the keynote, we also announced that the React 18 RC is available to try now. Pending further feedback, this is the exact version of React that we will publish to stable early next year.
\\nTo try the React 18 RC, upgrade your dependencies:
\\nnpm install react@rc react-dom@rc
and switch to the new createRoot
API:
// before
const container = document.getElementById(\'root\');
ReactDOM.render(<App />, container);
// after
const container = document.getElementById(\'root\');
const root = ReactDOM.createRoot(container);
root.render(<App/>);
For a demo of upgrading to React 18, see Shruti Kapoor’s talk here:
\\n\\nReact 18 also includes improvements to server-side rendering performance using Suspense.
\\nStreaming server rendering lets you generate HTML from React components on the server, and stream that HTML to your users. In React 18, you can use Suspense
to break down your app into smaller independent units which can be streamed independently of each other without blocking the rest of the app. This means users will see your content sooner and be able to start interacting with it much faster.
For a deep dive, see Shaundai Person’s talk here:
\\n\\nFor React 18, we created our first Working Group to collaborate with a panel of experts, developers, library maintainers, and educators. Together we worked to create our gradual adoption strategy and refine new APIs such as useId
, useSyncExternalStore
, and useInsertionEffect
.
For an overview of this work, see Aakansha’ Doshi’s talk:
\\n\\nTo support the new features in this release, we also announced the newly formed React DevTools team and a new Timeline Profiler to help developers debug their React apps.
\\nFor more information and a demo of new DevTools features, see Brian Vaughn’s talk:
\\n\\nLooking further into the future, Xuan Huang (黄玄) shared an update from our React Labs research into an auto-memoizing compiler. Check out this talk for more information and a demo of the compiler prototype:
\\n\\nRachel Nabors kicked off a section of talks about learning and designing with React with a keynote about our investment in React’s new docs (now shipped as react.dev):
\\n\\nWe also heard talks on learning and designing with React:
\\nTalks from the Relay, React Native, and PyTorch teams:
\\nAnd talks from the community on accessibility, tooling, and Server Components:
\\nThis was our first year planning a conference ourselves, and we have a lot of people to thank.
\\nFirst, thanks to all of our speakers Aakansha Doshi, Andrew Clark, Brian Vaughn, Daishi Kato, Debbie O’Brien, Delba de Oliveira, Diego Haz, Eric Rozell, Helen Lin, Juan Tejada, Lauren Tan, Linton Ye, Lyle Troxell, Rachel Nabors, Rick Hanlon, Robert Balicki, Roman Rädle, Sarah Rainsberger, Shaundai Person, Shruti Kapoor, Steven Moyes, Tafu Nakazaki, and Xuan Huang (黄玄).
\\nThanks to everyone who helped provide feedback on talks including Andrew Clark, Dan Abramov, Dave McCabe, Eli White, Joe Savona, Lauren Tan, Rachel Nabors, and Tim Yung.
\\nThanks to Lauren Tan for setting up the conference Discord and serving as our Discord admin.
\\nThanks to Seth Webster for feedback on overall direction and making sure we were focused on diversity and inclusion.
\\nThanks to Rachel Nabors for spearheading our moderation effort, and Aisha Blake for creating our moderation guide, leading our moderation team, training the translators and moderators, and helping to moderate both events.
\\nThanks to our moderators Jesslyn Tannady, Suzie Grange, Becca Bailey, Luna Wei, Joe Previte, Nicola Corti, Gijs Weterings, Claudio Procida, Julia Neumann, Mengdi Chen, Jean Zhang, Ricky Li, and Xuan Huang (黄玄).
\\nThanks to Manjula Dube, Sahil Mhapsekar, and Vihang Patel from React India, and Jasmine Xie, QiChang Li, and YanLun Li from React China for helping moderate our replay event and keep it engaging for the community.
\\nThanks to Vercel for publishing their Virtual Event Starter Kit, which the conference website was built on, and to Lee Robinson and Delba de Oliveira for sharing their experience running Next.js Conf.
\\nThanks to Leah Silber for sharing her experience running conferences, learnings from running RustConf, and for her book Event Driven and the advice it contains for running conferences.
\\nThanks to Kevin Lewis and Rachel Nabors for sharing their experience running Women of React Conf.
\\nThanks to Aakansha Doshi, Laurie Barth, Michael Chan, and Shaundai Person for their advice and ideas throughout planning.
\\nThanks to Dan Lebowitz for help designing and building the conference website and tickets.
\\nThanks to Laura Podolak Waddell, Desmond Osei-Acheampong, Mark Rossi, Josh Toberman and others on the Facebook Video Productions team for recording the videos for the Keynote and Meta employee talks.
\\nThanks to our partner HitPlay for helping to organize the conference, editing all the videos in the stream, translating all the talks, and moderating the Discord in multiple languages.
\\nFinally, thanks to all of our participants for making this a great React Conf!
June 8, 2021 by Andrew Clark, Brian Vaughn, Christine Abernathy, Dan Abramov, Rachel Nabors, Rick Hanlon, Sebastian Markbåge, and Seth Webster
\\nThe React team is excited to share a few updates:
These updates are primarily aimed at maintainers of third-party libraries. If you’re learning, teaching, or using React to build user-facing applications, you can safely ignore this post. But you are welcome to follow the discussions in the React 18 Working Group if you’re curious!
When it’s released, React 18 will include out-of-the-box improvements (like automatic batching), new APIs (like startTransition
), and a new streaming server renderer with built-in support for React.lazy
.
These features are possible thanks to a new opt-in mechanism we’re adding in React 18. It’s called “concurrent rendering” and it lets React prepare multiple versions of the UI at the same time. This change is mostly behind-the-scenes, but it unlocks new possibilities to improve both real and perceived performance of your app.
\\nIf you’ve been following our research into the future of React (we don’t expect you to!), you might have heard of something called “concurrent mode” or that it might break your app. In response to this feedback from the community, we’ve redesigned the upgrade strategy for gradual adoption. Instead of an all-or-nothing “mode”, concurrent rendering will only be enabled for updates triggered by one of the new features. In practice, this means you will be able to adopt React 18 without rewrites and try the new features at your own pace.
\\nSince concurrency in React 18 is opt-in, there are no significant out-of-the-box breaking changes to component behavior. You can upgrade to React 18 with minimal or no changes to your application code, with a level of effort comparable to a typical major React release. Based on our experience converting several apps to React 18, we expect that many users will be able to upgrade within a single afternoon.
\\nWe successfully shipped concurrent features to tens of thousands of components at Facebook, and in our experience, we’ve found that most React components “just work” without additional changes. We’re committed to making sure this is a smooth upgrade for the entire community, so today we’re announcing the React 18 Working Group.
\\nWe’re trying something new for this release: We’ve invited a panel of experts, developers, library authors, and educators from across the React community to participate in our React 18 Working Group to provide feedback, ask questions, and collaborate on the release. We couldn’t invite everyone we wanted to this initial, small group, but if this experiment works out, we hope there will be more in the future!
\\nThe goal of the React 18 Working Group is to prepare the ecosystem for a smooth, gradual adoption of React 18 by existing applications and libraries. The Working Group is hosted on GitHub Discussions and is available for the public to read. Members of the working group can leave feedback, ask questions, and share ideas. The core team will also use the discussions repo to share our research findings. As the stable release gets closer, any important information will also be posted on this blog.
\\nFor more information on upgrading to React 18, or additional resources about the release, see the React 18 announcement post.
\\nEveryone can read the discussions in the React 18 Working Group repo.
\\nBecause we expect an initial surge of interest in the Working Group, only invited members will be allowed to create or comment on threads. However, the threads are fully visible to the public, so everyone has access to the same information. We believe this is a good compromise between creating a productive environment for working group members, while maintaining transparency with the wider community.
\\nAs always, you can submit bug reports, questions, and general feedback to our issue tracker.
\\nNew alphas are regularly published to npm using the @alpha
tag. These releases are built using the most recent commit to our main repo. When a feature or bugfix is merged, it will appear in an alpha the following weekday.
There may be significant behavioral or API changes between alpha releases. Please remember that alpha releases are not recommended for user-facing, production applications.
\\nWe don’t have a specific release date scheduled, but we expect it will take several months of feedback and iteration before React 18 is ready for most production applications.
\\nMore details about our projected release timeline are available in the Working Group. We’ll post updates on this blog when we’re closer to a public release.
December 21, 2020 by Dan Abramov, Lauren Tan, Joseph Savona, and Sebastian Markbåge
\\n2020 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.
\\n\\nReact 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!
\\nIf you want to check them out, we recommend going in the following order:
\\nWatch the talk to learn about React Server Components and see the demo.
\\nClone the demo to play with React Server Components on your computer.
\\nRead the RFC (with FAQ at the end) for a deeper technical breakdown and to provide feedback.
\\nWe 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!