The essentials
Quick reference
One focused task per row. Jump to the related section for complete, working examples.
| Use | Syntax | Examples |
|---|---|---|
| Start a transition | document.startViewTransition(() => updateDOM()); | View examples |
| Provide an API fallback | document.startViewTransition ? document.startViewTransition(update) :
update(); | View examples |
| Name a shared element | .hero { view-transition-name: product-hero; } | View examples |
| Exclude an element | .live-clock { view-transition-name: none; } | View examples |
| Style group geometry | ::view-transition-group(product-hero) {
animation-duration: 360ms;
} | View examples |
| Animate the old capture | ::view-transition-old(product-hero) {
animation: fade-out 180ms ease-in both;
} | View examples |
| Animate the new capture | ::view-transition-new(product-hero) {
animation: fade-in 240ms ease-out both;
} | View examples |
| Customize the root cross-fade | ::view-transition-old(root) {
animation: fade-out 160ms linear both;
} | View examples |
| Control image-pair blending | ::view-transition-image-pair(card) {
isolation: isolate;
} | View examples |
| Opt in navigation | @view-transition { navigation: auto; } | View examples |
| Declare navigation types | @view-transition {
navigation: auto;
types: forwards backwards;
} | View examples |
| Match an active type | :active-view-transition-type(forwards) {
--slide-direction: 1;
} | View examples |
| Class several captures | .tile { view-transition-class: tile; } | View examples |
| Style a capture class | ::view-transition-group(*.tile) {
animation-duration: 300ms;
} | View examples |
| Respect reduced motion | @media (prefers-reduced-motion: reduce) {
::view-transition-group(*) { animation-duration: 1ms; };
} | View examples |
| Limit expensive capture area | .card { contain: paint; view-transition-name: card; } | View examples |
View transitions separate an immediate document-state change from an optional animated rendering of that change. The browser captures old and new visual states into generated pseudo-elements, while the real DOM moves directly to its correct state. Use the API as progressive enhancement, assign unique names only to meaningful shared elements, keep the update callback focused on DOM state, and disable nonessential motion when the user requests reduced motion. Cross-document features and Level 2 selectors remain evolving, so test current target browsers.
Step by step
Detailed examples
Wrap a same-document state change, not application logic
document.startViewTransition(updateCallback) captures the old state, invokes the callback, captures the new state, and animates between them. The callback may be asynchronous, but rendering waits for it, so complete only the state update required for the next visual state. Feature-detect the method and always execute the update without animation when it is absent or a transition is skipped.
const button = document.querySelector('[data-layout-toggle]');
button.addEventListener('click', () => {
const update = () => document.body.classList.toggle('compact');
if (document.startViewTransition) {
document.startViewTransition(update);
} else {
update();
}
}); Capture shared elements under unique names
The root document transitions by default. Assign view-transition-name only when an element needs independent geometry or animation. During one captured state, each custom name must resolve to at most one rendered element or the transition is skipped. Keep names stable across old and new states, and do not use captures to hide incorrect focus order or DOM semantics.
.product__image { view-transition-name: product-hero; }
.product__title { view-transition-name: product-title; }
.live-clock { view-transition-name: none; }
::view-transition-group(product-hero) {
animation-duration: 360ms;
animation-timing-function: cubic-bezier(.2, .8, .2, 1);
} Target the generated transition tree deliberately
Each named capture creates a group, image-pair, old, and new pseudo-element. The group interpolates layout geometry; old and new carry captured imagery and default cross-fade animations. Style the narrowest level that expresses the effect, and account for cases where only old or only new exists because an element is entering or leaving.
::view-transition-old(product-hero) {
animation: hero-leave 180ms ease-in both;
}
::view-transition-new(product-hero) {
animation: hero-enter 280ms ease-out both;
}
@keyframes hero-leave { to { opacity: 0; scale: .96; } }
@keyframes hero-enter { from { opacity: 0; scale: 1.04; } } Customize timing without fighting the capture model
Animation timing set on a group inherits through the generated tree in the user-agent styles. Replace old and new animations together when you need a custom cross-fade, and retain fill modes so captures do not flash at their endpoints. The transition layer paints above ordinary page content, so avoid long-running or interaction-blocking effects.
::view-transition-image-pair(root) { isolation: isolate; }
::view-transition-old(root) { animation: fade-out 160ms linear both; }
::view-transition-new(root) { animation: fade-in 220ms linear both; }
@keyframes fade-out { to { opacity: 0; } }
@keyframes fade-in { from { opacity: 0; } } Share pseudo-element styles across many captures
Level 2 view-transition-class works like a styling class for generated transition pseudo-elements, but it does not capture an element by itself. Each element still needs a unique view-transition-name. The *.class selector form avoids repeating identical group rules across a collection whose items transition independently.
#tile-a { view-transition-name: tile-a; }
#tile-b { view-transition-name: tile-b; }
.tile { view-transition-class: tile; }
::view-transition-group(*.tile) {
animation-duration: 300ms;
animation-timing-function: ease-out;
} Preserve user control, focus, and rendering performance
A transition is visual enhancement, not a substitute for correct focus management, live-region behavior, or navigation feedback. Reduce or eliminate nonessential animation under prefers-reduced-motion. Capture only elements that materially improve continuity; large named subtrees consume memory, and filters, blending, or many simultaneous captures increase rendering cost.
@media (prefers-reduced-motion: reduce) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation-duration: 1ms;
animation-delay: 0s;
}
}
.product-card { contain: paint; } Local code tester
Prepare shared elements for view transitions
Inspect the named capture rules and reduced-motion guard; animation requires an eligible state change or navigation.
Sources and further reading
References
Authoritative documentation used to verify and expand this cheat sheet.
Help us improve
Found a typo or missing example?
Tell us what would make this cheat sheet clearer, more complete, or more useful.



