What are View Transitions?
The View Transitions API provides a way to create smooth animated transitions between different states of a web page. It works by capturing a snapshot of the current state (the "old" state), allowing you to update the DOM, then capturing a snapshot of the new state and animating between the two. The browser creates a tree of pseudo-elements — ::view-transition, ::view-transition-group, ::view-transition-image-pair, ::view-transition-old, and ::view-transition-new — that you can style with standard CSS animation properties to customize the transition effect.
The API provides two distinct modes. Same-document transitions let you animate between DOM state changes within a single page — you call document.startViewTransition() and the browser automatically captures old and new states, then crossfades between them. This is ideal for SPAs, tab switches, and state changes within a page. Cross-document transitions (MPA mode) work across full page navigations: by adding the @view-transition CSS at-rule with navigation: auto, the browser animates the transition between two separate pages automatically, without any JavaScript at all.
What makes view transitions truly powerful is named transitions. By assigning a view-transition-name to specific elements, you can make them transition independently from the rest of the page. For example, a thumbnail image on a listing page and a hero image on a detail page can share the same view-transition-name, and the browser will animate the thumbnail smoothly expanding into the hero image — a "shared element transition" that previously required complex JavaScript animation libraries to achieve.
How to use this tool
Adjust the duration slider to control how long the transition takes, from 100ms for snappy interactions to 2000ms for dramatic effects. Pick an easing function from the dropdown to control the animation curve — ease is a good default, ease-out feels natural for entering elements, and ease-in works well for exiting elements. Select "custom" to enter your own cubic-bezier value for precise control over the timing curve.
Set a transition name in the name field. The value "root" applies the transition to the entire page (this is the default behavior). To create targeted element transitions, use a custom name like "hero" or "card" and assign the same view-transition-name to the corresponding element in your CSS. Click "Play Transition" to see a live preview that toggles between two states with your configured animation. Copy the CSS output and add it to your stylesheet to use the transition in your project.
Practical examples
Page-level crossfade transition
The simplest view transition: a crossfade between the entire old and new page states. This works for both same-document and cross-document transitions and provides an immediate upgrade over hard page cuts.
/* Enable cross-document transitions for MPA sites */
@view-transition {
navigation: auto;
}
/* Customize the default crossfade timing */
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 300ms;
animation-timing-function: ease-in-out;
}
/* For same-document transitions, trigger with JavaScript */
/* document.startViewTransition(() => updateDOM()); */
Shared element transition
A shared element transition animates a specific element from its position and size in the old state to its position and size in the new state. This creates the illusion that the same element is moving between pages or views — for example, a thumbnail expanding into a full-size hero image.
/* On the listing page: assign a name to the thumbnail */
.product-thumbnail {
view-transition-name: product-image;
}
/* On the detail page: assign the same name to the hero image */
.product-hero {
view-transition-name: product-image;
}
/* Customize the shared element animation */
::view-transition-group(product-image) {
animation-duration: 400ms;
animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
/* Crossfade the old and new images during the transition */
::view-transition-old(product-image),
::view-transition-new(product-image) {
animation-duration: 250ms;
}
Named view transition for independent element animation
Different elements on the page can have different transition animations. By assigning unique view-transition-name values to specific elements, you can control each one independently — for example, sliding the header while fading the content.
/* Each element gets its own transition name */
.site-header {
view-transition-name: header;
}
.main-content {
view-transition-name: content;
}
.sidebar {
view-transition-name: sidebar;
}
/* Header slides down */
::view-transition-old(header) {
animation: 300ms ease-out slide-up;
}
::view-transition-new(header) {
animation: 300ms ease-out slide-down;
}
/* Content crossfades (default behavior) */
::view-transition-old(content),
::view-transition-new(content) {
animation-duration: 200ms;
}
/* Sidebar slides from the right */
::view-transition-new(sidebar) {
animation: 400ms ease-out slide-from-right;
}
@keyframes slide-down {
from { transform: translateY(-100%); }
}
@keyframes slide-up {
to { transform: translateY(-100%); }
}
@keyframes slide-from-right {
from { transform: translateX(100%); }
}
Common patterns and best practices
View transitions are straightforward to implement, but these patterns will help you create polished, performant animations.
- Start with the default crossfade. Before customizing animations, add view transitions with the default behavior. The built-in crossfade looks good in most cases and requires no custom keyframes.
- Keep durations short. Transitions between 150ms and 400ms feel responsive. Anything over 500ms starts to feel sluggish. Use shorter durations for small UI changes and slightly longer ones for page-level transitions.
- Ensure unique
view-transition-namevalues. Every element with aview-transition-namemust have a unique name on the page. If two visible elements share the same name during a transition, the transition will fail. Use dynamic names (via inline styles or CSS custom properties) for list items. - Use
view-transition-name: noneto opt elements out. If an element should not participate in the transition animation, explicitly set itsview-transition-nametonone. - Handle the
prefers-reduced-motionpreference. Wrap your transition styles in@media (prefers-reduced-motion: no-preference)so users who prefer reduced motion see instant state changes instead of animations. - Use
view-transition-classfor shared styles. Instead of writing the same animation rules for multiple named transitions, assign a shared class withview-transition-classand target it with::view-transition-group(*.class-name).
Browser support
Same-document view transitions (using document.startViewTransition()) are supported in Chrome 111+ (March 2023), Edge 111+, and Safari 18+ (September 2024). Cross-document (MPA) view transitions are supported in Chrome 126+ (June 2024) and Edge 126+. Firefox has same-document view transitions in active development behind a flag. For production use, always check for support with if (document.startViewTransition) in JavaScript or @supports (view-transition-name: test) in CSS. The API is designed for progressive enhancement: if a browser does not support view transitions, the DOM update still happens instantly without animation, so your application remains fully functional.
FAQ
Do I need JavaScript to use view transitions?
It depends on the mode. For same-document transitions, yes — you call document.startViewTransition() and update the DOM inside its callback. The browser handles the animation automatically, but you need JavaScript to trigger it. For cross-document (MPA) transitions, you only need the @view-transition CSS at-rule with navigation: auto, and the browser handles everything during normal page navigations — no JavaScript required.
Can I use this for SPA frameworks like React?
Yes. Libraries like next-view-transitions for Next.js and Astro's built-in view transition support make integration seamless. For other frameworks, you can call document.startViewTransition() manually and wrap your state updates inside the callback. React 18+'s concurrent features work well with this pattern since the DOM update can happen synchronously inside the transition callback.
What is the difference between same-document and cross-document view transitions?
Same-document transitions animate DOM changes within a single page using JavaScript. You call document.startViewTransition(callback), make your DOM updates inside the callback, and the browser captures the before and after states to animate between them. This is the mode used by SPAs and dynamic web applications. Cross-document (MPA) transitions animate between full page navigations using only CSS — you add @view-transition { navigation: auto; } to both the source and destination pages, and the browser automatically crossfades between them during navigation. No JavaScript is needed for cross-document transitions.
How do I debug view transitions?
Chrome DevTools has a dedicated View Transitions panel accessible under the Animations tab (or via the three-dot menu in the Performance panel). It lets you pause an in-progress transition, slow it down to inspect each frame, and view the ::view-transition pseudo-element tree in the Elements panel. You can inspect the computed styles of ::view-transition-old and ::view-transition-new pseudo-elements, see their dimensions and positioning, and adjust animation properties live. This is invaluable for fine-tuning timing and debugging layout issues during transitions.
Can I have different animations for different elements?
Yes, assign unique view-transition-name values to elements and target them individually with ::view-transition-old(name) and ::view-transition-new(name) pseudo-elements. Each named element gets its own independent animation. For example, you can make a header slide while the main content fades, and a sidebar scales in from the side — all within the same transition. Use ::view-transition-group(name) to control the group-level animation (size and position interpolation) for shared element transitions.