What's new in view transitions (2025 update)

Published: Oct 08, 2025

A lot has changed since we shipped same-document view transitions in 2023.

In 2024 we shipped cross-document view transitions, added refinements such as view-transition-class and view transition types, and also welcomed Safari in adding support for view transitions.

This post gives you an overview of what changed for view transitions in 2025.

Better browser and framework support

Same-document view transitions are about to become Baseline Newly available

Browser Support

  • Chrome: 111.
  • Edge: 111.
  • Firefox Technology Preview: supported.
  • Safari: 18.

Source

A focus area of Interop 2025 is the View Transition API, specifically same-document view transitions with document.startViewTransition(updateCallback), view-transition-class, auto-naming with view-transition-name: match-element, and the :active-view-transition CSS selector.

Firefox intends to include these features in the upcoming Firefox 144 release which becomes stable on October 14, 2025. This will make these features Baseline Newly available.

Support for the View Transition API is now in React core

Throughout the year, the React team has been working on adding view transitions to the core of React. They announced react@experimental support back in April and this week, at React Conf, they moved support of it into react@canary which means the design is close to final.

function Child() {
  return (
    <ViewTransition>
      <div>Hi</div>
    </ViewTransition>
  );
}

function Parent() {
  const [show, setShow] = useState();
  if (show) {
    return <Child />;
  }
  return null;
}

You can check out React's <ViewTransition> docs for all details or watch this talk by Aurora Scharff for a good introduction on the topic.

Recently shipped features

Auto-name elements with view-transition-name: match-element

Browser Support

  • Chrome: 137.
  • Edge: 137.
  • Firefox Technology Preview: supported.
  • Safari: not supported.

To mark an element to be snapshotted as part of a view transition, you give it a view-transition-name. This is key to view transitions, as it unlocks features such as transitioning between two different elements. Each element has the same view-transition-name value at each side of the transition, and view transitions take care of things for you.

However, uniquely naming elements can become cumbersome when transitioning a lot of elements that don't really change. If you have 100 elements that move in a list, you have to come up with 100 unique names.

Thanks to match-element you don't need to do all that. When using this as the value for view-transition-name, the browser will generate an internal view-transition-name for every matched element based on the element's identity.

In the following demo this approach is used. Each card in the row gets an automatically generated view-transition-name.

.card {
  view-transition-name: match-element;
  view-transition-class: card;
}

#targeted-card {
  view-transition-name: targeted-card;
  view-transition-class: none;
}

The one card that enters or exits gets an explicit name. That name is used in the CSS to attach specific animations onto that snapshot. The snapshots of all other cards are styled using the view-transition-class associated with them.

/* Style all pseudos with the class .card */
::view-transition-group(*.card) {
  animation-timing-function: var(--bounce-easing);
  animation-duration: 0.5s;
}

/* Style only the targeted-card's pseudos */
::view-transition-old(targeted-card):only-child {
  animation: animate-out ease-out 0.5s forwards;
}
::view-transition-new(targeted-card):only-child {
  animation: animate-in ease-in 0.25s forwards;
}

DevTools now shows rules targeting pseudos that use a view-transition-class

To debug view transitions, you can use the Animations panel from DevTools to pause all animations. This gives you time to inspect the pseudo-elements without having to worry that the view transition will reach its finished state. You can even manually scrub through the animation's timelines to see how the transitions play out.

Debugging view transitions with Chrome DevTools.

Previously, when inspecting one of the ::view-transition-* pseudo-elements, Chrome DevTools did not expose rules targeted to pseudos using their set view-transition-class. This changed in Chrome 139, which added the functionality.

Figure: Screenshot of Chrome DevTools inspecting a view-transition-group pseudo-element in the cards demo. The styles tab shows the rules affecting that pseudo, including the rule that uses the view-transition-group(*.card) selector.

Nested view transition groups are available from Chrome 140

Browser Support

  • Chrome: 140.
  • Edge: not supported.
  • Firefox: not supported.
  • Safari: not supported.

When a view transition runs, it renders the snapshotted elements in a tree of pseudo-elements. By default, the generated tree is "flat". This means that the original hierarchy in the DOM is lost, and all captured view transition groups are siblings under a single ::view-transition pseudo-element.

This flat tree approach is sufficient for many use-cases, but becomes problematic when visual effects such as clipping or 3D transforms are used. These require some hierarchy in the tree.

Recording of a demo without and with nested view transition groups enabled. When nested view transition groups are used, the text contents can remain clipped by the card and the text also rotate in 3D along with that card.

Thanks to "Nested view transition groups" you can now nest ::view-transition-group pseudo-elements within each other. When view transition groups are nested, it's possible to restore effects such as clipping during the transition.

Learn how to use view transition groups

Pseudo-elements now inherit more animation properties

When you set one of the animation-* longhand properties on a ::view-transition-group(…) pseudo, the contained ::view-transition-image-pair(…), ::view-transition-old(…), and ::view-transition-new(…) also inherit that property. This is handy because it automatically keeps the cross-fade between the ::view-transition-new(…) and ::view-transition-old(…)in sync with the ::view-transition-group(…).

::view-transition-group(.card) {
  animation-duration: 0.5s;
}

Initially this inheritance was limited to animation-duration and animation-fill-mode (and later animation-delay as well), but now this has been extended to inherit more animation longhands.

Browsers that support view transitions, now should have the following in their user-agent stylesheet now

:root::view-transition-image-pair(*),
:root::view-transition-old(*),
:root::view-transition-new(*) {
  animation-duration: inherit;
  animation-fill-mode: inherit;
  animation-delay: inherit;
  animation-timing-function: inherit;
  animation-iteration-count: inherit;
  animation-direction: inherit;
  animation-play-state: inherit;
}

The pseudo-elements inheriting more properties shipped in Chrome 140.

The execution of the finished promise callback no longer waits for a frame

When using the finished promise to execute a callback, Chrome used to wait for a frame to be produced before executing that logic. This could cause a flicker at the end of the animation when script moves some styles around in an attempt to preserve a visually similar state.

 document.startViewTransition(() => {
  if (from) {
    dfrom.classList.remove("shadow");
    dto.appendChild(target);
  } else {
    dto.classList.remove("shadow");
    dfrom.appendChild(target);
  }
}).finished.then(() => {
  if (from) {
    dto.classList.add("shadow");
  } else {
    dfrom.classList.add("shadow");
  }
  from = 1 - from;
});
Recording of the previous code in action, recorded in Chrome 139 that does not include the timing fix. You can see a glitch when the box's shadow gets added when the transition has finished.

This change in timing rectifies the situation by moving the view transition clean up steps to run asynchronously after the lifecycle is completed. This ensures that the visual frame produced at finished promise resolution still maintains the view transition structure, thereby avoiding the flicker.

This timing change shipped in Chrome 140.

Upcoming features

The year hasn't finished yet, so there's still some time to ship some more features.

Scoped view transitions are ready for testing in Chrome 140

Scoped view transitions is a proposed extension to the View Transition API which lets you start a view transition on a subtree of the DOM by calling element.startViewTransition() (instead of document.startViewTransition()) on any HTMLElement.

Scoped transitions allow multiple view transitions to run at the same time (as long as they have different transition roots). Pointer events are prevented on only that subtree (which you can re-enable), rather than the whole document. In addition, they let elements outside of the transition root be painted on top of the scoped view transition.

In the following demo, you can move a dot within its container by clicking one of the buttons. This can be done with document-scoped view transitions or element-scoped view transitions, allowing you to see how they behave differently.

The feature is ready for testing from Chrome 140 with the "Experimental Web Platform features" flag enabled in chrome://flags. We're actively looking for developer feedback.

The position of ::view-transition will change from fixed to absolute in Chrome 142

The ::view-transition pseudo is currently positioned using position: fixed. Following a CSS Working Group resolution this will change to position: absolute.

This position value change from fixed to absolute–which is coming to Chrome 142–is not visually observable because the containing block of the ::view-transition pseudo is the snapshot containing block anyway. The only observable effect is a different position value when doing a getComputedStyle.

document.activeViewTransition is coming to Chrome 142

When a view transition is started, a ViewTransition instance gets created. This object contains several promises and functionality to track the transition progress, as well as allow manipulations such as skipping the transition or modifying its types.

Instead of requiring you to manually keep track of this instance, Chrome now provides a document.activeViewTransition property that represents this object. If no transition is ongoing, its value is null.

For same-document view transitions, the value gets set when you call document.startViewTransition. For cross-document view transitions, you can access that ViewTransition instance in the pageswap and pagereveal events.

Support for document.activeViewTransition is about to ship in Chrome 142.

Prevent a view transition from automatically finishing with ViewTransition.waitUntil

A view transition automatically reaches its finished state when all its animations are finished. To prevent this automatic finishing, you can soon use ViewTranistion.waitUntil. When passing in a promise, the ViewTransition will only reach its finished state when that passed promise is also resolved.

const t = document.startViewTransition();
t.waitUntil(async () => {
  await fetch();
});

This change is coming later this year and will allow to, for example, await a fetch or unlock the easier implementation of a scroll-driven view transition.

What's next

As you can see we haven't been sitting still since we initially shipped View Transitions in 2023. We're looking forward to shipping scoped view transitions in the future and, as always, are open to feedback.

If you have a question about view transitions, reach out to us on social media. Feature requests for view transitions can be filed at the CSS WG. Should you run into a bug, file a Chromium bug to let us know.