View Transitions
When to Use
Use View Transitions for smooth animated transitions between UI states — page navigations, tab switches, content updates — without JS animation libraries. Use a JS library when you need complex choreographed sequencing.
Decision
| If you need... | Use... | Why |
|---|---|---|
| Animate a same-document state change | document.startViewTransition(callback) |
JS triggers, browser handles the animation |
| Animate cross-document navigation (MPA) | @view-transition { navigation: auto; } |
CSS-only, both pages opt in |
| Morph a specific element between states | view-transition-name: unique-name |
Browser matches named elements and morphs them |
| Complex choreographed animation | JS animation library | View Transitions handle simple morph/fade; not sequencing |
| Override the default cross-fade | ::view-transition-old() and ::view-transition-new() pseudo-elements |
Target old/new snapshot layers with CSS |
Pattern
Same-document (SPA-style) — any DOM update wrapped in the callback gets a cross-fade:
document.startViewTransition(() => {
updateDOM(); // synchronous DOM change
});
/* Morph a card image into a full-screen hero */
.card__image { view-transition-name: hero-image; }
.hero__image { view-transition-name: hero-image; }
/* Browser morphs the element between these two states */
/* Override default animation */
::view-transition-old(hero-image),
::view-transition-new(hero-image) {
animation-duration: 0.5s;
}
Cross-document (MPA) — add to both the outgoing and incoming page:
@view-transition {
navigation: auto;
}
This single CSS rule on both pages gives a default cross-fade navigation. No JavaScript.
Browser support:
- Same-document: Chrome 111, Firefox 144 (October 2025), Safari 18. Now baseline newly available (all major browsers).
- Cross-document: Chrome 126, Safari 18.2. Firefox does not yet support cross-document view transitions. Use @supports for cross-document.
Common Mistakes
- Wrong: Using non-unique
view-transition-namevalues → Right: Each name must be unique in the document at any given time; duplicate names cause the transition to skip that element - Wrong: Assigning
view-transition-nameto elements not visible in both states → Right: The browser skips the morph if one snapshot is missing; animation falls back to default cross-fade - Wrong: Long-running async operations inside
startViewTransitioncallback → Right: The old state is frozen while it runs; keep the callback fast or return a resolved promise quickly - Wrong: Using cross-document view transitions without checking Firefox support → Right: They silently fall back to instant navigation in Firefox