View Transitions API Patterns
Quick Guide: Use the View Transitions API for native page/state transitions.
document.startViewTransition()for same-document,@view-transition { navigation: auto }for cross-document MPA. Always feature-detect before use and respectprefers-reduced-motion. Use the options formstartViewTransition({ update, types })when you need typed transitions.
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST feature-detect before using startViewTransition - it is NOT available in all browsers)
(You MUST respect prefers-reduced-motion by providing reduced or disabled animations)
(You MUST ensure view-transition-name values are unique - duplicate names break transitions)
(You MUST clean up dynamically assigned view-transition-name values after transitions complete)
(You MUST use named constants for all animation timing values - NO magic numbers)
</critical_requirements>
Auto-detection: View Transitions API, startViewTransition, view-transition-name, @view-transition, ::view-transition, pageswap, pagereveal, ViewTransition, view-transition-class, match-element, active-view-transition-type
When to use:
- Animating state changes in single-page applications
- Creating smooth page-to-page transitions in multi-page applications
- Implementing shared element (hero) animations between views
- Providing visual continuity during navigation
- Creating custom transition effects (slide, scale, circular reveal)
Key patterns covered:
- Same-document transitions with startViewTransition()
- Cross-document MPA transitions with @view-transition CSS
- view-transition-name for shared element animations
- Pseudo-element styling (::view-transition-old, ::view-transition-new)
- Direction-aware transitions with :active-view-transition-type()
- Feature detection and graceful fallbacks
- prefers-reduced-motion accessibility patterns
When NOT to use:
- Complex physics-based animations (use animation libraries)
- Animations requiring precise timeline control
- Simple hover/focus effects (use CSS transitions)
Detailed Resources:
- examples/core.md - Feature detection, state transitions, promise handling, CSS customization
- examples/spa.md - Theme switcher, form steps, tab panels, list reordering
- examples/shared-elements.md - Hero animations, multiple shared elements, MPA shared elements, modals
- reference.md - Decision frameworks, pseudo-element reference, browser support, anti-patterns
<philosophy>
Philosophy
The View Transitions API provides a native browser mechanism for creating animated transitions between DOM states or pages. It captures "before" and "after" snapshots, overlays them as pseudo-elements, and animates between them.
Core principles:
- Native over library - Browser-native transitions are more performant and require less JavaScript
- Progressive enhancement - Always feature-detect and provide functional fallback
- Snapshot-based - Old state is captured as a screenshot, new state as a live representation
- CSS-driven - Customize animations through pseudo-element CSS, not JavaScript
- Accessibility-first - Always respect prefers-reduced-motion user preferences
<patterns>
Core Patterns
Pattern 1: Feature Detection with Fallback
Always check for API support before using View Transitions. See examples/core.md Pattern 1 for full utility.
const SUPPORTS_VIEW_TRANSITIONS =
typeof document !== "undefined" && "startViewTransition" in document;
function updateWithTransition(updateFn: () => void | Promise<void>): void {
if (!SUPPORTS_VIEW_TRANSITIONS) {
updateFn();
return;
}
document.startViewTransition(() => updateFn());
}
Why good: Prevents runtime errors in unsupported browsers, provides seamless fallback
Pattern 2: Same-Document (SPA) Transitions
Animate DOM state changes within a single page. See examples/core.md Patterns 2-5 for state transitions, async loading, promise handling, and skip logic.
// startViewTransition accepts a callback or an options object
const transition = document.startViewTransition(async () => {
await updateFn();
});
// Options form - set types for CSS targeting
const transition = document.startViewTransition({
update: () => updateDOM(),
types: ["slide-forward"],
});
await transition.finished;
ViewTransition object provides three promises:
| Promise | Resolves when |
|---|---|
transition.ready | Pseudo-element tree created |
transition.updateCallbackDone | DOM update callback completed |
transition.finished | Animation complete |
Pattern 3: Cross-Document (MPA) Transitions
Enable transitions between separate pages without JavaScript. Both pages must opt in.
/* Include on BOTH source and destination pages */
@view-transition {
navigation: auto;
}
Why good: No JavaScript required, works for traverse/push/replace navigations
Obsolete syntax: <meta name="view-transition" content="same-origin"> - use the CSS at-rule instead.
Pattern 4: Shared Element Transitions
Create hero animations by giving matching elements the same view-transition-name. See examples/shared-elements.md for full product list-to-detail, multi-element, and MPA examples.
:root {
--hero-duration: 300ms;
--hero-easing: cubic-bezier(0.4, 0, 0.2, 1);
}
/* Same name on both pages/states creates shared element animation */
.product-thumbnail {
view-transition-name: product-hero;
}
.product-image {
view-transition-name: product-hero;
}
::view-transition-group(product-hero) {
animation-duration: var(--hero-duration);
animation-timing-function: var(--hero-easing);
}
Key rules: Names must be unique across the document. Clean up dynamically assigned names after transition.finished.
Pattern 5: Custom CSS Animations
Override default cross-fade with custom animations via pseudo-elements. See examples/core.md Pattern 6 for full examples.
:root {
--transition-duration: 300ms;
--transition-easing: ease-in-out;
}
::view-transition-old(root) {
animation: slide-out-left var(--transition-duration) var(--transition-easing);
}
::view-transition-new(root) {
animation: slide-in-right var(--transition-duration) var(--transition-easing);
}
Why good: CSS custom properties for timing constants, GPU-accelerated transforms
Pattern 6: Direction-Aware Transitions
Use different animations for forward vs backward navigation. Use the types parameter or ViewTransition.types set.
html:active-view-transition-type(forwards) {
&::view-transition-old(content) {
animation-name: slide-out-left;
}
&::view-transition-new(content) {
animation-name: slide-in-right;
}
}
html:active-view-transition-type(backwards) {
&::view-transition-old(content) {
animation-name: slide-out-right;
}
&::view-transition-new(content) {
animation-name: slide-in-left;
}
}
// Preferred: set types via options parameter
document.startViewTransition({
update: () => navigateForward(),
types: ["forwards"],
});
// Alternative: mutate types set on existing transition
const transition = document.startViewTransition(updateFn);
transition.types.add("forwards");
See examples/spa.md for form step and tab panel examples.
Pattern 7: Accessibility - Reduced Motion
Always respect user preferences for reduc