On this page
CSS View Transitions API: Progressive Enhancement for Page and State Animations
What are view transitions?
The View Transitions API lets the browser animate between two states of a page. It takes a snapshot of the old view, applies your DOM or page change, takes a snapshot of the new view and then cross-fades or animates between them. It works for same-document changes (for example, switching a theme, reordering a list or opening a detail view in a single-page app) and, in supporting browsers, for cross-document navigation between normal multi-page website pages.
Focus keyword: View Transitions API. Related trending keywords: document.startViewTransition, view-transition-name, @view-transition navigation auto, ::view-transition-old and ::view-transition-new, shared element transition, page transition CSS, MPA view transitions, view-transition-class.
Why treat it as progressive enhancement?
Browser support differs between same-document and cross-document transitions, and between browsers and versions. The safe approach is: your site must work exactly the same without the animation. If the browser does not support it, the content simply updates instantly. Always feature-detect in JavaScript, use @supports or the at-rule where appropriate, and verify support on Can I use before you depend on the feature.
Same-document transitions (SPA or JS state changes)
Wrap your DOM update in document.startViewTransition(callback). The browser captures the old state, runs the callback to update the DOM, captures the new state, and animates between them. A safe wrapper looks like this:
- If
document.startViewTransitiondoes not exist, run the update directly. - Otherwise call
document.startViewTransition(() => update()).
The returned object has promises: ready (pseudo-elements are created, animation is about to start), finished (animation complete) and updateCallbackDone. It also has skipTransition().
The pseudo-element tree
::view-transition: root overlay.::view-transition-group(name): handles position and size movement.::view-transition-image-pair(name): holds old and new snapshots.::view-transition-old(name): snapshot before the change.::view-transition-new(name): snapshot after the change.
The default for the whole page is the name root, a simple cross-fade. You can restyle it with normal CSS animations, for example: ::view-transition-old(root) { animation: fade-out .3s; }.
view-transition-name: shared element transitions
Give an element a unique view-transition-name in both states (like hero-image). The browser will morph its size and position between the old and new versions, which creates the popular shared element or hero transition. Rules: names must be unique on the page at the moment of capture, and duplicates cause the transition to be skipped. For lists, set a name per item using inline styles or attr() alternatives, or use view-transition-class to apply shared styling to many named elements.
Cross-document (multi-page) transitions
For normal navigations between same-origin pages, opt in with CSS on both pages: @view-transition { navigation: auto; }. Then use view-transition-name on elements that exist on both pages to morph them. Events such as pageswap and pagereveal let you customize the transition with JavaScript. Keep pages fast: the transition waits for the new page to be ready to render, so slow pages will feel slow.
View transition types
You can pass types (for example forward and back) and target them with :active-view-transition-type(forward) in CSS to use different animations per direction.
Accessibility and performance
- Respect
prefers-reduced-motion: disable or shorten custom animations, or keep only a simple fade. - Keep durations short (about 200ms to 400ms).
- During a transition, the page shows snapshots, so the old content is not interactive. Keep it brief.
- Focus management still matters. After the update, move focus to a sensible place and update the page title for SPAs.
- Do not rely on it to communicate state. Use normal, accessible DOM changes.
Quick summary
Use the View Transitions API to add polished state and page animations with very little code. Feature-detect, keep the no-animation path fully functional and respect reduced motion.
Press Run to execute.
Press Run to execute.
Press Run to execute.
Press Run to execute.
Wrap the DOM update in document.startViewTransition, but only when the API exists and the user does not prefer reduced motion. Give every list item a unique view-transition-name so items slide to their new positions.
Press Run to execute.
Show expected output
Clicking Reverse smoothly slides the list items into their new order in supporting browsers, and instantly reorders them in browsers without support or when reduced motion is on.This is a self-check — compare your result with the expected output above.
Was this page helpful?