On this page
View Transitions
Animate between states (same-document) or between pages (cross-document) with a single CSS opt-in. Replaces ad-hoc SPA animation libraries with a platform primitive.
What it is
The View Transitions API animates the change from one DOM state to another - or from one document to the next - by taking a snapshot of the old state, applying the change, taking a snapshot of the new state, and cross-fading between them. There are two flavours.
Same-document (JS-driven). A single-page-app pattern. Wrap any DOM mutation in document.startViewTransition():
document.startViewTransition(() => {
// any DOM update - render new view, swap routes, etc.
app.navigate(targetUrl);
});
Cross-document (declarative). Two normal HTML pages opt into a shared transition with a single CSS rule on each:
@view-transition {
navigation: auto;
}
When the user navigates from page A to page B and both pages have this rule, the browser captures A, replaces it with B, and cross-fades. No JavaScript required.
This site ships it. global.css carries @view-transition { navigation: auto }, so cross-document navigations between any two pages on webspecification.com cross-fade on supporting browsers. The matching prefers-reduced-motion: reduce rule zeroes animation-duration on the view-transition pseudo-elements, so reduced-motion users get an instant swap. Pairs with our Speculation Rules prerender - the next page is already in memory, then it animates in.
You shape the animation with the ::view-transition-* pseudo-elements:
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 200ms;
}
/* Tag elements that should animate independently */
.hero {
view-transition-name: hero;
}
Why it matters
- Replaces a stack of dependencies. Framer Motion, GSAP page transitions, hand-rolled FLIP - replaced by a CSS declaration and a pseudo-element.
- Bridges MPA and SPA UX. Cross-document view transitions give a traditional multi-page site the feel of an SPA without an SPA’s complexity, JavaScript weight, or accessibility regressions.
- Pairs with Speculation Rules. A prerendered next page + a cross-document view transition is the closest the platform gets to “instant, animated” navigation.
- Respects user preferences. Browsers honour
prefers-reduced-motion: reduceand skip the animation. You should still check it explicitly when you customise.
How to implement
Cross-document, the minimum:
@view-transition {
navigation: auto;
}
Ship that on every page you want to participate. The browser handles the rest.
Same-document, the minimum:
if (!document.startViewTransition) {
updateDom();
return;
}
document.startViewTransition(() => updateDom());
Feature-detect - older browsers run the callback synchronously without the transition.
Tag persistent elements so they morph instead of cross-fading:
.product-card[data-id="42"] { view-transition-name: product-42; }
The same name on both old and new DOM signals “this is the same conceptual element - animate the position/size delta.”
Respect reduced motion:
@media (prefers-reduced-motion: reduce) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation: none !important;
}
}
Keep transitions short - 150–250ms. Longer feels sluggish; shorter feels like a flash.
Common mistakes
- Same
view-transition-nameon multiple elements at once. The browser logs an error and skips the morph. - Animating layout-dependent properties (width, height) when you could animate transforms. Transforms compositor-only, layout properties trigger reflow.
- Long animations on data-heavy navigations. The user is waiting for the page, not for your animation. Cap at ~250ms.
- Ignoring
prefers-reduced-motion. People with vestibular disorders should not be motion-tested for a route change. - Same-document transitions that swallow exceptions in the callback. Wrap with try/catch and re-throw on transition rejection.
CSS View Transitions Level 2 & Top-Layer Pseudo-Tree Isolation
Implementing same-document and cross-document view transitions across modern web applications requires understanding browser pseudo-element rendering:
- Declarative Cross-Document Opt-In (
@view-transition): Enable seamless multi-page application (MPA) page transitions using CSS@view-transition { navigation: auto; }. When navigating between same-origin pages, the browser automatically captures live snapshots of the outgoing and incoming documents, rendering a top-layer cross-fade. - Top-Layer Pseudo-Element Tree Isolation: Captured elements morph via top-layer pseudo-elements (
::view-transition-group(name),::view-transition-old(name),::view-transition-new(name)). Because transition elements temporarily break out of DOM stacking contexts and parentoverflow: hiddencontainers, check for clipping issues during element expansion. - Enforcing Unique
view-transition-nameIdentifiers: Every element assigned aview-transition-nameMUST be unique on the active DOM page. Duplicate names on the same screen cause the browser engine to reject the transition and fall back to an instant layout update.
Verification
- DevTools → Animations panel. Records every view transition with timing.
caniuse.com/view-transitions- current browser support matrix. Same-document is broadly available; cross-document still rolling out (Chromium first, WebKit shipping, Gecko in progress).- Toggle
prefers-reduced-motion: reducein DevTools → Rendering. The transition must disappear, not just shorten. - Run a Lighthouse audit on the destination page - adding view transitions must not regress LCP or INP. If it does, your animation is too long or too expensive.