View Transitions API

The View Transitions API is a browser platform feature for animating between two states of the DOM — a filtered list changing, a component expanding, or a full navigation from one page to another — with smooth, app-like transitions that previously required heavy JavaScript or a framework's animation layer. You tell the browser "capture the current state, I'll update the DOM, now animate between them," and it handles the crossfade or morph automatically.

Its bigger significance is cross-document transitions: for the first time, plain multi-page websites (server-rendered, with real navigations between separate HTML documents) can have the seamless animated transitions that used to be the exclusive selling point of single-page apps. This narrows a long-standing gap — you can get app-like polish without adopting an SPA architecture. This page covers both same-document and cross-document use.

TL;DR

Quick Example

Wrap a DOM update in startViewTransition and the browser animates the change — no manual FLIP calculations, no animation library:

For cross-document transitions on a multi-page site, you often write no JavaScript at all — just opt in with CSS:

With that one rule, navigating between two pages of a traditional server-rendered site crossfades smoothly instead of the usual hard cut.

Core Concepts

How It Works: Snapshot, Update, Animate

The API's model is simple and consistent across use cases:

  1. Capture — the browser takes a visual snapshot of the current (old) state.
  2. Update — your callback changes the DOM to the new state (or, for cross-document, the new page loads).
  3. Animate — the browser snapshots the new state and animates from old to new (a crossfade by default).

You're freed from the hardest part of transition animations — measuring elements before and after a change and interpolating between them (the "FLIP" technique). The browser does that measurement and tweening for you.

Same-Document vs Cross-Document

Cross-document transitions are the headline addition — they let multi-page and static sites feel like apps without becoming SPAs.

Named Transitions and Morphing

By default the whole page crossfades. To make a specific element animate independently — say a list thumbnail that should smoothly morph into the detail page's hero image — give both the same view-transition-name:

The browser recognizes them as the same conceptual element across the change and tweens position, size, and appearance — the polished "shared element transition" that made native apps feel fluid, now on the web. Each view-transition-name must be unique per page.

Customizing the Animation

The transition is driven by CSS: the browser exposes pseudo-elements (::view-transition-old(name), ::view-transition-new(name), and group/root variants) that you can target with normal CSS animations to control duration, easing, and effect — slide instead of fade, direction-aware transitions, and so on.

Best Practices

Treat It as Progressive Enhancement

Always feature-detect (if (document.startViewTransition)) and ensure the DOM update still happens without it. In unsupported browsers the change should apply instantly — no animation, but no breakage. Never gate functionality on the transition running.

Respect Reduced Motion

Some users are sensitive to motion. Honor prefers-reduced-motion: disable or minimize view transitions for those users via a media query, keeping the instant update. Motion is polish, not a requirement.

Keep Transitions Fast and Purposeful

Good transitions are short (well under a second) and communicate a relationship — this list item became this detail view. Long or gratuitous animations slow users down and annoy. Use morphing (view-transition-name) to show continuity, and a simple crossfade otherwise.

Use Unique, Stable Transition Names

Each view-transition-name must be unique on a page. For lists, generate names per item (e.g. from an ID) so the right thumbnail morphs into the right hero — a duplicated or mismatched name breaks the effect.

Don't Block the Update on Slow Work

The DOM-update callback should be quick. For cross-document transitions, a slow-loading next page delays the animation. Keep the transitioning update snappy so the animation feels responsive.

Common Mistakes

Gating the DOM Update Behind the API

Ignoring prefers-reduced-motion

Shipping motion to everyone, including motion-sensitive users, is an accessibility miss. Disable or reduce transitions under prefers-reduced-motion: reduce.

Reusing a Transition Name Across Elements

Two elements sharing a view-transition-name on the same page (e.g. every card in a list using card) breaks the animation — names must be unique. Generate per-item names from stable IDs.

FAQ

What does the View Transitions API actually do?

It animates between two states of the page. You tell the browser to capture the current view, run a DOM update (or navigate), and the browser animates from the old snapshot to the new one — a crossfade by default, or a morph for named elements. It replaces the complex, manual work of measuring elements before and after a change and interpolating between them.

Does it work for regular multi-page websites, or just SPAs?

Both — and the multi-page support is the big deal. With a single CSS rule (@view-transition { navigation: auto }), traditional server-rendered or static multi-page sites get animated transitions between full navigations, often with no JavaScript. This brings the seamless, app-like feel that used to require a single-page-app architecture to plain MPAs.

How do I make one element morph into another?

Give both elements the same view-transition-name in CSS — for example a list thumbnail and the detail page's hero image both named hero-image. The browser treats them as the same conceptual element across the change and animates position, size, and appearance between them. Names must be unique per page, so generate per-item names for lists.

What happens in browsers that don't support it?

Nothing breaks if you use it as progressive enhancement: feature-detect document.startViewTransition, and when it's absent just apply the DOM update directly. The user sees an instant change with no animation. Never make functionality depend on the transition itself — treat the animation as an enhancement layered on a working update.

Can I customize the animation beyond a crossfade?

Yes. The browser exposes pseudo-elements (::view-transition-old(), ::view-transition-new(), and group/root variants) that you style with ordinary CSS animations to control duration, easing, and effect — slides, direction-aware motion, custom morphs. The default is a crossfade; CSS lets you make it whatever the design needs.

Related Topics

References