Smooth Web Morphs with Chromium's View Transitions API

This article explores the inner workings of the View Transitions API in the Chromium browser and how it delivers seamless, animated morphs during web navigations. By capturing visual snapshots of Document Object Model (DOM) states and leveraging a specialized pseudo-element tree, the API eliminates jarring page loads and state changes. Below, you will learn the exact rendering mechanics of this process and how Chromium unifies the transition experience across both Single-Page Applications (SPAs) and Multi-Page Applications (MPAs).

The Core Mechanism: Snapshots and Live Representations

Chromium creates smooth morphs by temporarily decoupling the visual presentation of the webpage from the underlying DOM structure.

When a transition is triggered, the browser captures a rasterized snapshot (essentially a flat image texture) of the current page state. Next, the DOM is updated in the background while the user still sees the static snapshot on their screen. Once the new DOM state is rendered and ready, Chromium captures the new visual state and generates a temporary overlay to animate the differences between the old snapshot and the new live view.

The Pseudo-Element Tree

To execute the animation without disrupting the actual layout of the page, Chromium constructs a temporary overlay using a specific hierarchy of CSS pseudo-elements:

  • ::view-transition: The root overlay that sits on top of the entire webpage during the animation.
  • ::view-transition-group: The container that animates the size, position, and transformation matrix between the old and new states.
  • ::view-transition-image-pair: A housing container for the two visual states that provides isolation for CSS blending modes.
  • ::view-transition-old: The static screenshot of the previous state, which typically fades out.
  • ::view-transition-new: The live representation of the new state, which typically fades in.

Because these elements exist in their own top-layer rendering tree, Chromium can animate them smoothly on the compositor thread, avoiding expensive layout recalculations.

How the "Morph" Effect Works

By default, Chromium applies a simple cross-fade to the entire viewport. True morphing occurs when developers assign the view-transition-name CSS property to specific elements.

When an element in the old state shares the exact same view-transition-name as an element in the new state, Chromium isolates them from the main viewport transition and places them into their own dedicated ::view-transition-group.

The browser records the bounding box (width, height, and screen coordinates) of the element in the old state. It then calculates the bounding box of the matching element in the new state. Chromium automatically generates a CSS keyframe animation that interpolates the position and dimensions from the old box to the new box, while simultaneously cross-fading the old rasterized texture into the new live element. This creates the illusion that a single element is seamlessly morphing its shape and location.

Single-Page Application (SPA) Navigations

In SPAs, navigations and state changes occur within the same document via JavaScript. Chromium handles same-document transitions through the document.startViewTransition() method using the following sequence:

  1. The developer calls document.startViewTransition() and passes a callback function designed to update the DOM.
  2. Chromium immediately captures the "old" visual snapshot of the current page and any named elements.
  3. Chromium pauses visual rendering of the page and executes the developer's callback function to mutate the DOM.
  4. Once the DOM update finishes, Chromium records the "new" state of the page and its named elements.
  5. The browser constructs the pseudo-element tree, places it over the viewport, and runs the morph and cross-fade animations.
  6. When the animation completes, Chromium destroys the pseudo-element tree, revealing the newly updated, fully interactive DOM underneath.

Multi-Page Application (MPA) Navigations

Historically, smooth transitions between two entirely different HTML documents were impossible because the browser had to destroy the first document before rendering the second. Chromium solves this for same-origin navigations purely through CSS, requiring no JavaScript.

By adding the @view-transition { navigation: auto; } rule to the CSS of both the outgoing and incoming pages, Chromium alters its standard navigation lifecycle:

  1. When a user clicks a link to navigate to a new page on the same origin, Chromium intercepts the unload event and takes a snapshot of the outgoing document, recording the metadata of any elements with a view-transition-name.
  2. The browser retains this snapshot and layout data in memory while it fetches, parses, and loads the new HTML document in the background.
  3. Chromium holds the old snapshot on the user's screen until the new document's render-blocking resources are loaded and the new page is ready to be painted.
  4. The browser scans the new document for matching view-transition-name properties.
  5. Chromium constructs the ::view-transition pseudo-element tree inside the new document, injecting the retained snapshot from the previous page into the ::view-transition-old pseudo-element.
  6. The morph animation plays seamlessly across the document boundary, completely masking the full page reload.