View Transitions API Examples

The View Transitions API animates between two states of a page. In a single-page app, wrap the DOM update in document.startViewTransition(() => update()). Between pages on the same origin, add @view-transition { navigation: auto; } to both pages. Give an element a unique view-transition-name and the browser morphs it from its old position to its new one.

Two kinds of view transition
Same-document (SPA)Cross-document (MPA)
How you start itdocument.startViewTransition()@view-transition { navigation: auto; } on both pages
Chrome and Edge111 and later126 and later
Safari18 and later18.2 and later
Firefox144 and laterNot supported yet
BaselineNewly available (October 2025)Not Baseline

Updated

Most explanations of view transitions describe the pseudo-element tree and stop. This page starts with a working demo in your browser: shuffle a grid, open a card into a detail view, and watch named elements move. Then it gives the code for each pattern, the cross-page setup, and the fallbacks for browsers without support.

Live demo

Shuffle the grid or open a card. Each tile has its own view-transition-name, so the browser animates it to its new position.

Same-document transitions

Feature-detected startViewTransition

Browsers without the API run the update straight away, so nothing breaks.

function updateView(update) {
  if (!document.startViewTransition) {
    update();
    return;
  }
  document.startViewTransition(update);
}

button.addEventListener("click", () => {
  updateView(() => {
    list.classList.toggle("grid");
  });
});

Morph a thumbnail into a hero

Only one element may carry the name at a time, so set it on the clicked thumbnail just before the transition.

.thumb.is-active,
.detail-hero {
  view-transition-name: hero;
}

::view-transition-group(hero) {
  animation-duration: 350ms;
  animation-timing-function: cubic-bezier(.2, .8, .2, 1);
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none;
  }
}

React

startViewTransition with flushSync

flushSync makes React commit the new DOM inside the callback.

import { useState } from "react";
import { flushSync } from "react-dom";

export function Gallery({ items }) {
  const [open, setOpen] = useState(null);
  const go = (next) => {
    if (!document.startViewTransition) return setOpen(next);
    document.startViewTransition(() => flushSync(() => setOpen(next)));
  };
  return open ? (
    <article onClick={() => go(null)}>
      <img src={open.src} alt={open.alt} style={{ viewTransitionName: "hero" }} />
    </article>
  ) : (
    <ul>
      {items.map((it) => (
        <li key={it.id} onClick={() => go(it)}>
          <img src={it.src} alt={it.alt} />
        </li>
      ))}
    </ul>
  );
}

Cross-document transitions

Slide between pages of a multi-page site

Include this in the stylesheet of every page that should take part. Same origin only.

@view-transition {
  navigation: auto;
}

@keyframes slide-out { to { transform: translateX(-30%); opacity: 0; } }
@keyframes slide-in { from { transform: translateX(30%); opacity: 0; } }

::view-transition-old(root) {
  animation: 250ms ease-in both slide-out;
}
::view-transition-new(root) {
  animation: 250ms ease-out both slide-in;
}

API reference

The pieces of the View Transitions API
NameKindWhat it does
document.startViewTransition(update)MethodStarts a same-document transition and returns a ViewTransition
ViewTransition.readyPromiseResolves when the pseudo-elements exist and animation is about to start
ViewTransition.finishedPromiseResolves when the animation ends and the new state is live
ViewTransition.updateCallbackDonePromiseResolves when your update callback has finished
ViewTransition.skipTransition()MethodSkips the animation; the update still runs
view-transition-nameCSS propertyGives an element its own snapshot to morph
@view-transitionCSS at-ruleOpts a page into cross-document transitions
::view-transition-old() / -new()Pseudo-elementsThe outgoing and incoming snapshots
::view-transition-group()Pseudo-elementAnimates size and position for one name
pageswap / pagerevealEventsLet script adjust a cross-document transition

What happens during a transition

The browser captures the current state as images, one for the page and one for every named element. It runs your update while rendering is paused, then captures the new state.

It builds a tree of pseudo-elements above the page: a group for each name, holding an old and a new image. By default the group animates size and position while the two images cross-fade, which is what creates the morph.

Naming elements well

A name links an element in the old state to one in the new state. A product photo in a list and the large photo on its detail page can share a name such as product-42, and the browser animates one into the other.

Set the name only where it is needed. Naming an element that does not change still costs a separate snapshot.

Types for different directions

startViewTransition also accepts an options object with an update function and a types array. A type such as forwards or backwards can then be matched in CSS with the :active-view-transition-type() selector, so going back slides the other way.

For cross-page navigations, the types descriptor inside @view-transition sets types in the same way.

How it works

  1. Press Shuffle to reorder the grid. Each tile has its own view-transition-name, so each one glides to its new place.
  2. Click a tile to open it. The colour block and title morph into the detail view because they share names with it.
  3. If your browser lacks the API, the demo still updates instantly and tells you so.
  4. Copy the snippets below for plain JavaScript, React and multi-page sites.

Tips

Frequently asked questions

What does document.startViewTransition do?

It snapshots the current page, runs your update callback, then animates from the old snapshot to the live new state. It returns a ViewTransition object with ready, finished and updateCallbackDone promises and a skipTransition() method.

How do I add view transitions between pages?

Put @view-transition { navigation: auto; } in the CSS of both the page you leave and the page you arrive on. The pages must share an origin, and by default you get a cross-fade. Give matching elements the same view-transition-name on both pages to morph them.

Do view transitions work in Firefox?

Same-document transitions work from Firefox 144. Cross-document transitions were not supported in Firefox at the time of writing, so on those browsers the navigation simply happens without animation.

How do I use view transitions in React?

Call document.startViewTransition and update state inside flushSync from react-dom, so React writes the new DOM synchronously before the browser takes the new snapshot.

How do I customise the animation?

Style the pseudo-elements. ::view-transition-old(name) is the outgoing snapshot and ::view-transition-new(name) the incoming one, and ::view-transition-group(name) controls size and position. Use root as the name for the whole page.

Related: CSS transitions playground, Respecting prefers-reduced-motion, Scroll animations, Framer Motion examples, Modern CSS features.

Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.

More in Guides

We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.

Product

Who It's For

Features

Resources

Company

View Sitemap