返回资讯中心
外部精选
大前端
#React

React 19.3

React 19.3 adds new features like View Transitions, Fragment Refs, browser(), Trusted Types, and more.

React BlogThe React Team30 分钟阅读

以下正文同步自 React Blog,版权归原站所有,已转换为易读排版。

Copy pageCopy

React 19.3

September 9, 2026 by The React Team

Last year, we shared View Transitions and Fragment Refs as new experimental APIs coming to React. We’re excited to announce that both of these are now stable in React 19.3!

In this post, we’ll go over how they work, and also cover some other notable changes in this release.

View Transitions

The new <ViewTransition> component lets you animate elements as they enter, exit, move, or resize using the browser’s View Transition API. We shared it as an experimental API last year, and in 19.3 it’s stable and ready to use.

To animate part of your UI, wrap it in <ViewTransition>:

import { ViewTransition } from &#x27;react&#x27;;

{isShowing && (

<ViewTransition>

<Component />

</ViewTransition>

)}

Now, whenever an update marked as a Transition changes the child component’s style, or causes the ViewTransition to be mounted or unmounted, React will animate that update.

React chooses which animation to run based on how the tree changed:

Note that updates not marked as Transitions don’t trigger animations, as those are meant to be urgent and reflected immediately in the UI. State updates inside of startTransition, a <Suspense> reveal, or an update from useDeferredValue all cause a View Transition to animate.

Here’s a simple example of an enter/exit animation:

ReloadClearFork

import { ViewTransition, useState, startTransition } from &#x27;react&#x27;;

import { Video } from &#x27;./Video&#x27;;

import videos from &#x27;./data&#x27;;

export default function Component() {

const [showItem, setShowItem] = useState(false);

return (

<>

<button

onClick={() => {

startTransition(() => {

setShowItem((prev) => !prev);

});

}}>

{showItem ? &#x27;➖&#x27; : &#x27;➕&#x27;}

</button>

{showItem && (

<ViewTransition>

<Video video={videos[0]} />

</ViewTransition>

)}

</>

);

}

Show more

By default, <ViewTransition> animates with a smooth cross-fade. You can customize each kind of animation by passing a View Transition Class and defining the animation in CSS, or you can use the Web Animations API to trigger animations imperatively with the event props (onEnter, onExit, onShare, onUpdate).

Currently, <ViewTransition> only works in the DOM. We’re working on support for React Native and other platforms.

Sometimes, you’ll want to customize which animation is used for the same state update. For example, navigating a carousel forward to the third slide should animate the slides right-to-left, while navigating it backward should animate them left-to-right, even though both actions set the currentSlide to 3.

You can customize the animation for a given View Transition by calling addTransitionType alongside the state update. This lets you add more information about the cause of a particular transition:

function nextSlide() {

startTransition(() => {

addTransitionType(&#x27;next&#x27;);

setCurrentSlide(c => c + 1);

});

}

function previousSlide() {

startTransition(() => {

addTransitionType(&#x27;previous&#x27;);

setCurrentSlide(c => c - 1);

});

}

Then, you can specify different animations based on that transition type:

<ViewTransition

enter={{

&#x27;next&#x27;: &#x27;from-right&#x27;,

&#x27;previous&#x27;: &#x27;from-left&#x27;,

}}

exit={{

&#x27;next&#x27;: &#x27;to-left&#x27;,

&#x27;previous&#x27;: &#x27;to-right&#x27;,

}}

>

<Page />

</ViewTransition>

Here’s an example:

ReloadClearFork

import {

ViewTransition,

addTransitionType,

useState,

startTransition,

Fragment

} from &#x27;react&#x27;;

import { Video } from &#x27;./Video&#x27;;

import videos from &#x27;./data&#x27;;

import &#x27;./animations.css&#x27;;

export default function Component() {

const [selected, setSelected] = useState(0)

const video = videos[selected];

return (

<>

<div className="button-container">

<button

onClick={() => {

startTransition(() => {

addTransitionType(&#x27;previous&#x27;);

setSelected(c => c > 0 ? c - 1 : videos.length - 1 )

});

}}>

⬅️

</button>

<button

onClick={() => {

startTransition(() => {

addTransitionType(&#x27;next&#x27;);

setSelected(c => c + 1 < videos.length ? c + 1 : 0)

});

}}>

➡️

</button>

</div>

<ViewTransition

key={video.id}

enter={{

&#x27;next&#x27;: &#x27;from-right&#x27;,

&#x27;previous&#x27;: &#x27;from-left&#x27;

}}

exit={{

&#x27;next&#x27;: &#x27;to-left&#x27;,

&#x27;previous&#x27;: &#x27;to-right&#x27;

}}

>

<Video video={video} />

</ViewTransition>

</>

);

}

Show more

React also adds every Transition Type to the element as a browser view transition type, so you can scope animations in CSS with :active-view-transition-type(...).

To learn more, see the addTransitionType docs.

One of the most exciting things about View Transitions in React is how they integrate with Suspense.

You can animate a Suspense boundary as it reveals its children by wrapping it in <ViewTransition>:

<ViewTransition>

<Suspense fallback={<Loading />}>

<Component />

</Suspense>

</ViewTransition>

When the children finish loading, React will trigger an update animation from the fallback to the final content.

Here’s an example. Try pressing ➕ to render a LazyVideo that suspends the first time it’s rendered:

ReloadClearFork

import { Suspense, useState, startTransition, use, ViewTransition } from &#x27;react&#x27;;

import { Video, VideoPlaceholder } from &#x27;./Video&#x27;;

import { fetchVideo } from &#x27;./data&#x27;;

export default function Component() {

const [showItem, setShowItem] = useState(false);

return (

<>

<button

onClick={() => {

startTransition(() => {

setShowItem((prev) => !prev);

});

}}

>

{showItem ? &#x27;➖&#x27; : &#x27;➕&#x27;}

</button>

{showItem && (

<ViewTransition>

<Suspense fallback={<VideoPlaceholder />}>

<LazyVideo />

</Suspense>

</ViewTransition>

)}

</>

);

}

function LazyVideo() {

const video = use(fetchVideo());

return <Video video={video} />;

}

Show more

While this works, you’ll notice that the video also animates in and out on subsequent reveals, even though it’s already been loaded. (You might also notice that the fallback fades in the first time it’s shown.)

In general, animations with Suspense work best when they’re used sparingly, and avoided for cached UI that would otherwise appear instantly.

Here are some principles for achieving good UX when animating with Suspense:

This keeps your app feeling snappy when things are already loaded, and only uses animation to make the update from fallback to final content more seamless.

To fix our example above, we can disable all animations other than updates:

<ViewTransition update="auto" default="none">

<Suspense fallback={<Fallback />}>

<Component />

</Suspense>

</ViewTransition>

Let’s see how it behaves now:

ReloadClearFork

import { Suspense, useState, startTransition, use, ViewTransition } from &#x27;react&#x27;;

import { Video, VideoPlaceholder } from &#x27;./Video&#x27;;

import { fetchVideo } from &#x27;./data&#x27;;

export default function Component() {

const [showItem, setShowItem] = useState(false);

return (

<>

<button

onClick={() => {

startTransition(() => {

setShowItem((prev) => !prev);

});

}}

>

{showItem ? &#x27;➖&#x27; : &#x27;➕&#x27;}

</button>

{showItem && (

<ViewTransition update="auto" default="none">

<Suspense fallback={<VideoPlaceholder />}>

<LazyVideo />

</Suspense>

</ViewTransition>

)}

</>

);

}

function LazyVideo() {

const video = use(fetchVideo());

return <Video video={video} />;

}

Show more

Notice how the fallback appears immediately when tapping the button, which keeps our UI feeling responsive to user actions. Additionally, once the video has been loaded, toggling it is instant.

There are other patterns you can use depending on what effect you want to achieve. To learn more, check out the docs on animating with Suspense.

In addition to animating fallbacks, View Transitions act as a way to opt images or fonts into triggering Suspense while they load.

This lets you avoid the browser’s default behavior where images or fonts may flicker in whenever they happen to finish loading, and instead build coordinated loading sequences that consider all of a component’s resources.

Wrap images or fonts inside of <ViewTransition> to trigger Suspense while they load:

<ViewTransition>

<Suspense fallback={<Fallback />}>

<img src={imageSrc} />

<style href={fontSrc} precedence="default">

{`@font-face {

font-family: &#x27;Fancy&#x27;;

src: url(${fontSrc}) format(&#x27;truetype&#x27;);

font-display: swap;

}`}

</style>

</Suspense>

</ViewTransition>

Here’s an example of a component that suspends until its data, image, and font have all loaded:

ReloadClearFork

import { ViewTransition, Suspense, use, useState, startTransition } from &#x27;react&#x27;;

import { fetchQuote } from &#x27;./data.js&#x27;;

import { freshStylesheetUrl, freshImageUrl } from &#x27;./resources.js&#x27;;

import { ProfileCard, ProfileCardLoading } from &#x27;./ProfileCard.js&#x27;;

import { VanillaProfileCard } from &#x27;./VanillaProfileCard.js&#x27;;

export default function App() {

const [resources, setResources] = useState(null);

return (

<>

<button

onClick={() => {

startTransition(() => {

setResources({

quotePromise: fetchQuote(),

stylesheet: freshStylesheetUrl(),

image: freshImageUrl(),

});

});

}}>

Show profile

</button>

{resources && (

<ViewTransition update=&#x27;auto&#x27; default=&#x27;none&#x27;>

<Suspense fallback={<ProfileCardLoading />}>

<ProfileCard resources={resources} />

</Suspense>

</ViewTransition>

)}

<hr />

<VanillaProfileCard />

</>

);

}

Show more

To learn more about waiting for images, fonts, or stylesheets to load, see the Suspense docs.

When you need lower-level control over a component’s DOM nodes—for example to attach an event listener, observe visibility, or move focus—you can usually use a ref. But there are some situations where this is difficult:

function Component() {

// How can we work with the list of DOM nodes rendered by this component?

return (

{posts.map(post => (

<Heading key={post.id}>

{post.title}

</Heading>

))}

)

}

Adding a wrapper <div> just to hold a ref sometimes works, but it can also interfere with your component’s styling or layout. Moreover, if a component doesn’t expose a ref prop, you would need to modify that component to do so, which might be impossible if it comes from a library you don’t control.

Fragment Refs solve these problems by providing a limited set of commonly used DOM methods that work with any React component, regardless of what it renders.

In 19.3, you can use them by passing a ref directly to a <Fragment>. This ref gives you a FragmentInstance, which you can use to work with the Fragment’s DOM children:

function Component() {

const fragmentRef = useRef(null);

useEffect(() => {

const fragmentInstance = fragmentRef.current;

fragmentInstance.focus();

}, []);

return (

<Fragment ref={fragmentRef}>

{posts.map(post => (

<Heading key={post.id}>

{post.title}

</Heading>

))}

</Fragment>

)

}

The FragmentInstance operates on the children’s DOM as a group, without changing its structure:

Thus, Fragment Refs let you attach behavior to other components without requiring you to modify those component’s internals, or without changing the DOM structure that they already produce.

This example shows an InView component with an onChange prop that fires whenever its children enter or exit the viewport:

ReloadClearFork

import { useState } from &#x27;react&#x27;;

import Card from &#x27;./Card&#x27;;

import InView from &#x27;./InView&#x27;;

export default function App() {

const [isVisible, setIsVisible] = useState(true);

return (

<div className={isVisible ? &#x27;page visible&#x27; : &#x27;page&#x27;}>

<div className="filler">Scroll down</div>

<InView onChange={setIsVisible}>

<Card title="First section" />

<Card title="Second section" />

</InView>

<div className

正文由 FLUX 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。