What is View Transitions API?
View Transitions API: The View Transitions API animates between two states of a page by snapshotting the old view, applying the change and crossfading or morphing to the new one.
document.startViewTransition(callback) takes a picture of the page, runs your callback to update the DOM, takes a second picture, then animates from the first to the second. By default it crossfades the whole page. Elements given a view-transition-name are captured separately and animate from their old position and size to their new ones, which produces the shared-element morph seen in native apps: a thumbnail growing into a hero image, a card sliding to its place in a reordered list. The animation runs on snapshots, so the real DOM can change in one step and the browser does the interpolation.
How it works
export function applyTheme(next: "light" | "dark") {
const update = () => document.documentElement.classList.toggle("dark", next === "dark")
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches
if (!document.startViewTransition || reduce) return update()
document.startViewTransition(update)
}
/* Crossfade speed for the whole page */
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 240ms;
}
/* A shared element: give it the same name in both states */
.cover { view-transition-name: cover; }
- Pseudo-elements
::view-transition-old(name)and::view-transition-new(name)hold the two snapshots and can be animated with ordinary CSS or the Web Animations API. - Names must be unique on the page at any moment. For lists, generate them from an id, such as
item-42. - The page is not interactive during the transition, so keep it under about 300ms.
- Cross-document transitions, between full page loads on the same origin, are opted into with
@view-transition { navigation: auto; }in CSS and no JavaScript. The same-document API is in most browsers; cross-document support arrived later and is worth checking for your audience.
With React and Next.js
React batches updates, so the state change has to be applied inside the callback, wrapped in flushSync when the DOM must be updated before the second snapshot. React has been adding a ViewTransition component that drives the same browser API from state and navigation changes; check the current React and Next.js docs for its status before depending on it. Until then, the plain API works in any client component.
Accessibility
A view transition is motion, so it belongs behind prefers-reduced-motion: skip the call, or reduce it to a quick fade. Focus is not moved by the transition; after a route change, still move focus to the new content as you otherwise would. Do not name text blocks that reflow, since a morphing paragraph is unreadable mid-flight; name images, cards and containers.
In MiniDev
The LightProvider material switch uses the API for its reveal: it starts a transition around the attribute change, then animates ::view-transition-new(root) with a circular clip-path that grows from the point of the click until the new material covers the page, and it falls back to an instant switch when the browser lacks the API or the user prefers reduced motion. ThemePicker is the natural place for the same trick with dark mode. More motion pieces are in the animation category.