View Transitions API
The View Transitions API lets the browser animate between two visual states of a page automatically — it snapshots the "before" and "after", then cross-fades and morphs between them — without you writing a single @keyframes rule by hand. Instead of manually choreographing every element that changes when, say, a list item is removed or a page navigates, you tell the browser "this DOM update should be a transition" and it works out a sensible default animation, which you can then customize with plain CSS.
document.startViewTransition() — the JS trigger
// Same-document ("SPA-style") view transition
function updateContent() {
document.startViewTransition(() => {
// Make your DOM changes inside this callback —
// e.g. swap out list items, change a class, update text.
document.querySelector('#panel').innerHTML = newHTML
})
}
// The browser automatically:
// 1. Screenshots the current state
// 2. Runs the callback to apply the new state
// 3. Screenshots the new state
// 4. Cross-fades between the two screenshotsStyling the transition with CSS
Once a transition is running, the browser exposes pseudo-elements representing the old and new snapshots, which you can style like any other element:
/* The outgoing (old) state */
::view-transition-old(root) {
animation: fade-out 0.3s ease forwards;
}
/* The incoming (new) state */
::view-transition-new(root) {
animation: fade-in 0.3s ease forwards;
}
@keyframes fade-out {
to { opacity: 0; }
}
@keyframes fade-in {
from { opacity: 0; }
}
/* Give a specific element its own named transition so it
morphs individually instead of being part of the root cross-fade */
.hero-image {
view-transition-name: hero;
}
/* Now ::view-transition-old(hero) / ::view-transition-new(hero)
can be targeted separately, and the browser will smoothly
morph the image's position and size between the two states */The default cross-fade is often good enough on its own — many teams call startViewTransition() and never touch these pseudo-elements at all. They exist for when you want a specific slide, zoom, or shared-element morph instead of the default fade.
Extending to cross-document navigations
Same-document view transitions cover SPA-style content swaps within one page load. The API is being extended to cross-document view transitions — animating between two separate full page navigations (e.g. clicking a link from a listing page to a detail page), which is opted into purely with CSS, no JavaScript required:
/* Opt both pages into cross-document view transitions */
@view-transition {
navigation: auto;
}
/* Then style ::view-transition-old/new exactly as with
same-document transitions, on either page's stylesheet */Same-document: JS-driven, works today for SPA content swaps and DOM updates.
Cross-document: CSS-driven opt-in (
@view-transition { navigation: auto; }), animates full page navigations.Both share the same ::view-transition-old/new styling model.