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
- The View Transitions API animates between two DOM states — the browser snapshots before/after and transitions between them, so you write far less animation code.
- Same-document transitions animate state changes within a page (SPA route changes, list filtering, expanding a card).
- Cross-document transitions animate real navigations between separate pages — bringing SPA-style smoothness to plain multi-page sites, often with zero JavaScript.
- The pattern: call
document.startViewTransition(() => updateDOM()); the browser captures old/new snapshots and crossfades by default. view-transition-nameon an element makes it a distinct transition target — so a thumbnail can morph into a hero image across the change.- It's a progressive enhancement: unsupported browsers just skip the animation and update instantly. Respect
prefers-reduced-motion.
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:
- Capture — the browser takes a visual snapshot of the current (old) state.
- Update — your callback changes the DOM to the new state (or, for cross-document, the new page loads).
- 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
- CSS — The language that customizes and drives the transitions
- Responsive Design — Motion and layout across devices
- Accessibility — Respecting reduced-motion and inclusive UX
- React — Framework routing that pairs with same-document transitions
- Progressive Web Apps — App-like web experiences the API enhances
- Web Performance — Keeping transitions fast and smooth
- Frontend Development — The broader platform