CSS View Transitions API: Multi-Page Animations

The CSS View Transitions API enables native, seamless animated transitions between different HTML documents in multi-page applications (MPAs) without requiring complex client-side JavaScript routing. This article explains how the API bridges navigation states, how browsers capture and animate page snapshots, and how developers can implement and customize cross-document transitions using modern CSS properties.

The Mechanism of Cross-Document Transitions

Traditionally, navigating between separate HTML pages resulted in a visible flash of white or an abrupt content replacement as the browser unloaded the current document and parsed the next. The View Transitions API solves this by capturing visual representations of both states and smoothly morphing between them.

During a cross-document navigation, the browser coordinates the transition through several distinct steps:

  1. Triggering Navigation: When a user clicks a link to a same-origin URL, the browser checks if both the current page and the destination page support view transitions.
  2. Snapshotting the Old State: Before discarding the current page, the browser takes a visual snapshot of the DOM elements participating in the transition.
  3. Fetching and Rendering the New State: The browser fetches the destination page and builds its DOM. Before painting it to the screen, the browser freezes the rendering pipeline and takes a snapshot of the new state.
  4. Generating Pseudo-Elements: The browser creates a temporary tree of pseudo-elements on the new document containing the old and new snapshots.
  5. Running the Animation: A default cross-fade animation runs using CSS keyframes, seamlessly transitioning from the old snapshot to the live new document.

Enabling Multi-Page Transitions

To activate cross-document view transitions, both the outgoing and incoming documents must explicitly opt in. This is achieved using the @view-transition at-rule in your stylesheet:

@view-transition {
  navigation: auto;
}

Both pages must belong to the same origin (protocol, domain, and port). If either page omits this rule or if the navigation is cross-origin, the browser falls back to standard page load behavior.

The Pseudo-Element Tree

Once activated, the browser constructs an ephemeral pseudo-element tree rooted at the document root:

  • ::view-transition: The root container that overlays the entire viewport.
  • ::view-transition-group(root): Controls the size and position of a specific transition group.
  • ::view-transition-image-pair(root): Houses both the outgoing and incoming visual states.
  • ::view-transition-old(root): A static snapshot of the previous page.
  • ::view-transition-new(root): A live representation of the incoming page.

By default, the browser interpolates the opacity of the old and new snapshots to create a smooth cross-fade, while adjusting dimensions and position if elements change size or location.

Animating Specific Elements with view-transition-name

While the default transition applies to the entire page (root), you can animate individual persistent elements—such as headers, hero images, or cards—by assigning them a unique view-transition-name:

.product-card-image {
  view-transition-name: product-image;
}

If an element on the destination page shares the same view-transition-name, the browser isolates those elements into their own dedicated ::view-transition-group(product-image) branch. The browser then automatically animates the element’s geometry, position, and visual properties across pages, creating an app-like shared element transition.

Customizing the Animation

Because view transitions are driven by standard CSS animations, you can override default timing functions, durations, or keyframes by targeting the transition pseudo-elements:

::view-transition-old(root) {
  animation: 300ms ease-out both slide-out-left;
}

::view-transition-new(root) {
  animation: 300ms ease-in both slide-in-right;
}

@keyframes slide-out-left {
  to { transform: translateX(-100%); }
}

@keyframes slide-in-right {
  from { transform: translateX(100%); }
}

By leveraging hardware-accelerated snapshot interpolation through pure CSS, the View Transitions API removes the need to convert multi-page architectures into single-page applications solely for polished navigation experiences.