PureDevTools

CSS View Transitions API Reference

Generate view transition CSS, explore pseudo-elements, and learn shared element morphing with interactive examples

All processing happens in your browser. No data is sent to any server.

Transition Type

Single element transition — assigns a named transition to one element

Animation Settings

Exit animation (old state)

Entrance animation (new state)

Generated Code

Element styles
/* Assign a unique name to enable view transition tracking */
.hero-image {
  view-transition-name: hero-image;
}
Pseudo-element animations
/* Outgoing state — screenshot of element before DOM update */
::view-transition-old(hero-image) {
  animation: 300ms ease-in-out fade-out;
}

/* Incoming state — live capture of element after DOM update */
::view-transition-new(hero-image) {
  animation: 300ms ease-in-out fade-in;
}

@keyframes fade-out {
  from { opacity: 1; }
  to   { opacity: 0; }
}

@keyframes fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
JavaScript
// Feature detection — graceful fallback for unsupported browsers
if (!document.startViewTransition) {
  updateDOM();
  return;
}

// Trigger the view transition
document.startViewTransition(() => {
  // Replace this with your actual DOM update:
  // e.g., toggle visibility, update content
  updateDOM();
});

// Async navigation example:
document.startViewTransition(async () => {
  const html = await fetch('/new-page').then(r => r.text());
  document.querySelector('main').innerHTML = html;
});

Your SPA switches between a product list and a product detail page. The transition is instant — the list vanishes, the detail page appears. It feels like a broken web app, not a native app. You know smooth page transitions exist (iOS has them, Android has them), but implementing them on the web traditionally means FLIP animations, requestAnimationFrame choreography, and managing intermediate DOM states. The View Transitions API does it in one document.startViewTransition() call and a few CSS rules.

Why This Reference (Not a Blog Post)

Most View Transitions tutorials show the basic cross-fade and stop. This reference covers view-transition-name assignment, the four ::view-transition-* pseudo-elements (group, image-pair, old, new), custom animations per element, multi-page transitions (MPA), element morphing (list item → detail hero), and reduced motion handling. Everything runs in your browser; no data is sent anywhere.

What Is the CSS View Transitions API?

The CSS View Transitions API lets you animate DOM changes — including full-page navigations — with smooth transitions between states. Without it, DOM updates are instant and jarring. With it, you can create native-app-quality animations: fade, slide, morph, or any combination, using only CSS and a single JavaScript call.

/* 1. Name the element you want to animate */
.hero-image {
  view-transition-name: hero-image;
}

/* 2. Customize the animation */
::view-transition-old(hero-image) {
  animation: 300ms ease-in fade-out;
}
::view-transition-new(hero-image) {
  animation: 300ms ease-out fade-in;
}
// 3. Wrap your DOM update in startViewTransition()
document.startViewTransition(() => {
  updatePageContent();
});

The browser captures the before and after states automatically — you only describe how to animate between them.

view-transition-name

The view-transition-name CSS property assigns a unique identifier to an element, telling the browser to track it across a view transition. Elements with matching names in both the old and new states are automatically morphed — their position, size, and appearance are interpolated.

/* Enable transition tracking for specific elements */
.card-title  { view-transition-name: card-title; }
.card-image  { view-transition-name: card-image; }
.navigation  { view-transition-name: nav; }

/* Opt out of transition */
.sidebar {
  view-transition-name: none; /* initial value — no tracking */
}

Rules:

::view-transition-old() and ::view-transition-new()

These pseudo-elements represent the two states of a transitioning element:

/* Exit animation — old state fades and slides left */
::view-transition-old(page-content) {
  animation: 250ms ease-in slide-to-left;
}

/* Entrance animation — new state slides in from right */
::view-transition-new(page-content) {
  animation: 300ms ease-out slide-from-right;
}

/* Target ALL named elements at once */
::view-transition-old(*),
::view-transition-new(*) {
  animation-duration: 200ms;
}

@keyframes slide-to-left   { to   { transform: translateX(-100%); } }
@keyframes slide-from-right { from { transform: translateX(100%); } }

Pseudo-element Tree

During each transition, the browser creates a temporary overlay with this structure:

::view-transition
└── ::view-transition-group(name)
    └── ::view-transition-image-pair(name)
        ├── ::view-transition-old(name)
        └── ::view-transition-new(name)

Shared Element Morphing

When the same view-transition-name exists in both the old and new states, the browser creates a morph transition — smoothly animating the element’s position, size, border-radius, and other geometric properties. This creates the “magic move” effect seen in native mobile apps.

/* List page: small card thumbnail */
.card-thumbnail {
  view-transition-name: product-hero;
  width: 120px;
  height: 90px;
  border-radius: 8px;
}

/* Detail page: full hero image */
.product-hero {
  view-transition-name: product-hero; /* same name — browser morphs it */
  width: 100%;
  height: 400px;
  border-radius: 0;
}

/* Customize the morph easing */
::view-transition-group(product-hero) {
  animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  animation-duration: 400ms;
}

Cross-Document Transitions (MPA)

View Transitions Level 2 adds support for multi-page application (MPA) navigation — transitions that work across full page loads without JavaScript:

/* Enable cross-document transitions in CSS */
@view-transition {
  navigation: auto; /* opt in to automatic cross-document transitions */
}

/* Customize same-origin navigation animations */
::view-transition-old(root) {
  animation: 200ms ease-in fade-out;
}

::view-transition-new(root) {
  animation: 300ms ease-out fade-in;
}

With navigation: auto, the browser transitions between pages automatically when the user clicks a link or navigates back/forward.

Reduced Motion

Always respect the user’s motion preference using @media (prefers-reduced-motion):

/* Default: full transition */
::view-transition-old(root) {
  animation: 300ms ease-in slide-out;
}

::view-transition-new(root) {
  animation: 300ms ease-out slide-in;
}

/* Reduced motion: use simple fade instead of slide */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation-duration: 0.01ms;
  }
}

Browser Support

The same-document View Transitions API (Level 1) is supported in Chrome 111+, Edge 111+, Opera 97+, and Safari 18+. Firefox does not yet support View Transitions as of early 2026. Cross-document transitions (Level 2) are supported in Chrome 126+ and Safari 18.2+.

/* Feature detection in CSS (Level 2 cross-document) */
@supports (view-transition-name: test) {
  /* View Transitions CSS is supported */
}
// Feature detection in JavaScript
if (!document.startViewTransition) {
  updateDOM(); // Instant update fallback
  return;
}

document.startViewTransition(() => updateDOM());

Use document.startViewTransition as a guard before calling the API. Unsupported browsers will perform instant DOM updates with no animation — a safe, functional fallback.

Common Patterns

Card grid to detail view:

/* Each card has a unique transition name */
.card[data-id="1"] { view-transition-name: card-1; }
.card[data-id="2"] { view-transition-name: card-2; }

/* Detail page hero uses the same name */
.detail-hero[data-id="1"] { view-transition-name: card-1; }

Navigation tabs:

.tab-content {
  view-transition-name: tab-content;
}
::view-transition-old(tab-content) {
  animation: 150ms ease-in fade-out;
}
::view-transition-new(tab-content) {
  animation: 150ms ease-out fade-in;
}

Theme switching:

:root {
  view-transition-name: theme;
}
/* Triggered by document.startViewTransition(() => toggleDarkMode()) */

Frequently Asked Questions

What is the CSS View Transitions API and how does it work? The View Transitions API lets you animate DOM changes with smooth CSS transitions. You wrap a DOM update in document.startViewTransition() — the browser captures the before state, applies your update, captures the after state, then plays CSS animations between them. You control the animations using ::view-transition-old() and ::view-transition-new() pseudo-elements.

What is view-transition-name and why must each name be unique? view-transition-name identifies an element for tracking across a transition. When two elements (old and new state) share the same name, the browser morphs between them automatically. Names must be unique because the browser uses each name to pair exactly one old snapshot with one new capture. Duplicate names cause the transition to abort for those elements.

What is the difference between same-document and cross-document view transitions? Same-document transitions (Level 1) animate changes within a single-page app — you trigger them with document.startViewTransition(). Cross-document transitions (Level 2) work across full page loads in a multi-page app using the CSS @view-transition { navigation: auto } rule without JavaScript. Cross-document support requires Chrome 126+ or Safari 18.2+.

Does Firefox support the View Transitions API? Firefox does not support View Transitions as of early 2026. Use if (!document.startViewTransition) as a guard. The DOM update will still happen — just without animation. This graceful degradation means View Transitions are purely progressive enhancement.

How do I prevent layout shift during a view transition? The browser overlays the transition on top of the page — layout does not shift. However, elements with view-transition-name are temporarily removed from the normal layout flow during the transition. Use contain: layout on named elements if you experience unexpected shifts, and always test with CSS transitions disabled to ensure the base layout is stable.

Can I animate with clip-path, blur, or other CSS properties? Yes. The ::view-transition-old() and ::view-transition-new() pseudo-elements support any animatable CSS property — opacity, transform, clip-path, filter (blur, brightness), border-radius, and more. Combine properties in a single @keyframes block for complex effects.

Related Tools

More CSS Tools