CSSView Transitions API

View Transitions API

The View Transitions API lets the browser animate between two visual states of a page automatically — it snapshots the "before" and "after", then cross-fades and morphs between them — without you writing a single @keyframes rule by hand. Instead of manually choreographing every element that changes when, say, a list item is removed or a page navigates, you tell the browser "this DOM update should be a transition" and it works out a sensible default animation, which you can then customize with plain CSS.

document.startViewTransition() — the JS trigger
This is primarily a JavaScript API
View Transitions are triggered from JavaScript, not from a CSS selector or property. CSS comes in afterward, as the styling surface for customizing how the transition looks. Think of it as: JS says when, CSS says how.

JS
// Same-document ("SPA-style") view transition
function updateContent() {
  document.startViewTransition(() => {
    // Make your DOM changes inside this callback —
    // e.g. swap out list items, change a class, update text.
    document.querySelector('#panel').innerHTML = newHTML
  })
}

// The browser automatically:
// 1. Screenshots the current state
// 2. Runs the callback to apply the new state
// 3. Screenshots the new state
// 4. Cross-fades between the two screenshots
Styling the transition with CSS

Once a transition is running, the browser exposes pseudo-elements representing the old and new snapshots, which you can style like any other element:

CSS
/* The outgoing (old) state */
::view-transition-old(root) {
  animation: fade-out 0.3s ease forwards;
}

/* The incoming (new) state */
::view-transition-new(root) {
  animation: fade-in 0.3s ease forwards;
}

@keyframes fade-out {
  to { opacity: 0; }
}
@keyframes fade-in {
  from { opacity: 0; }
}

/* Give a specific element its own named transition so it
   morphs individually instead of being part of the root cross-fade */
.hero-image {
  view-transition-name: hero;
}
/* Now ::view-transition-old(hero) / ::view-transition-new(hero)
   can be targeted separately, and the browser will smoothly
   morph the image's position and size between the two states */

The default cross-fade is often good enough on its own — many teams call startViewTransition() and never touch these pseudo-elements at all. They exist for when you want a specific slide, zoom, or shared-element morph instead of the default fade.

Extending to cross-document navigations

Same-document view transitions cover SPA-style content swaps within one page load. The API is being extended to cross-document view transitions — animating between two separate full page navigations (e.g. clicking a link from a listing page to a detail page), which is opted into purely with CSS, no JavaScript required:

CSS
/* Opt both pages into cross-document view transitions */
@view-transition {
  navigation: auto;
}

/* Then style ::view-transition-old/new exactly as with
   same-document transitions, on either page's stylesheet */
  • Same-document: JS-driven, works today for SPA content swaps and DOM updates.

  • Cross-document: CSS-driven opt-in (@view-transition { navigation: auto; }), animates full page navigations.

  • Both share the same ::view-transition-old/new styling model.

A newer, still-evolving API
View Transitions — especially the cross-document form — is a relatively recent addition to the platform and browser support varies. Treat it as a progressive enhancement: pages should look and function correctly with no transition at all, with the animated version as a bonus in supporting browsers.
Related
For a purely CSS way to animate an element based on scroll position (rather than a DOM-state change), see [Scroll-Driven Animations](/css/scroll-driven-animations).