Website Spec
← Performance
Recommended

View Transitions

Animate between states (same-document) or between pages (cross-document) with a single CSS opt-in. Replaces ad-hoc SPA animation libraries with a platform primitive.

What it is

The View Transitions API animates the change from one DOM state to another — or from one document to the next — by taking a snapshot of the old state, applying the change, taking a snapshot of the new state, and cross-fading between them. There are two flavours.

Same-document (JS-driven). A single-page-app pattern. Wrap any DOM mutation in document.startViewTransition():

document.startViewTransition(() => {
  // any DOM update — render new view, swap routes, etc.
  app.navigate(targetUrl);
});

Cross-document (declarative). Two normal HTML pages opt into a shared transition with a single CSS rule on each:

@view-transition {
  navigation: auto;
}

When the user navigates from page A to page B and both pages have this rule, the browser captures A, replaces it with B, and cross-fades. No JavaScript required.

You shape the animation with the ::view-transition-* pseudo-elements:

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 200ms;
}

/* Tag elements that should animate independently */
.hero {
  view-transition-name: hero;
}

Why it matters

How to implement

Cross-document, the minimum:

@view-transition {
  navigation: auto;
}

Ship that on every page you want to participate. The browser handles the rest.

Same-document, the minimum:

if (!document.startViewTransition) {
  updateDom();
  return;
}
document.startViewTransition(() => updateDom());

Feature-detect — older browsers run the callback synchronously without the transition.

Tag persistent elements so they morph instead of cross-fading:

.product-card[data-id="42"] { view-transition-name: product-42; }

The same name on both old and new DOM signals "this is the same conceptual element — animate the position/size delta."

Respect reduced motion:

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

Keep transitions short — 150–250ms. Longer feels sluggish; shorter feels like a flash.

Common mistakes

Verification

Sources