A route change does not have to feel like a hard cut. The browser's View Transitions API can animate the visual change between two rendered states, whether your app updates one document or navigates between separate pages. The key is to treat the transition as optional polish: the route or content update must still work when the browser does not support the feature or the visitor prefers less motion.
This guide shows the two approaches, how to add a named element transition, and what to check before using the API in production. It focuses on browser features and code rather than a framework-specific router.
Which kind of transition do you need?
| Site architecture | How a transition starts | Main API |
|---|---|---|
| Single-page app (SPA) | Your application updates the DOM within the current document | document.startViewTransition() |
| Multi-page site (MPA) | A same-origin link or history navigation loads another document, and both pages opt in | The CSS @view-transition rule |
These are related but separate features. An SPA router controls its own DOM updates, so it calls the JavaScript API. An MPA lets the browser perform a normal navigation; the CSS rule asks the browser to animate that navigation when it can. You do not need to intercept regular links to get an MPA transition.
Add a transition to a single-page app
Call document.startViewTransition() where your router commits the new view. Pass the function that updates the DOM as its callback:
function showRoute(nextRoute) {
const update = () => renderRoute(nextRoute);
if (typeof document.startViewTransition === "function") {
document.startViewTransition(update);
} else {
update();
}
}
Here, renderRoute() is the rendering function your app already uses. Keep route selection, URL changes, and history management in your router; wrap only the DOM update that should be animated. If the API is missing, the callback runs directly and the page still changes.
The browser captures the current visual state, runs the update callback, and then transitions to the new state. Do not wait for the animation to finish before changing the route: the callback is where the update belongs. The API returns a ViewTransition object if you need to coordinate later work with its lifecycle, but most route changes do not need to wait on it.
Set a short default animation and respect reduced motion
The browser supplies a default cross-fade for the root snapshot. You can adjust its duration and make it effectively immediate when a visitor has requested reduced motion:
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 180ms;
}
@media (prefers-reduced-motion: reduce) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation-duration: 1ms;
}
}
The prefers-reduced-motion media feature reflects the operating-system or browser preference. Keep the override in place even if your default animation is brief, and avoid adding large zooms or sliding panels without a clear reason. A transition should not delay interaction or make essential information available only through motion.
Opt into transitions between separate pages
For an MPA, include this rule in the stylesheet used by both the outgoing and incoming documents:
@view-transition {
navigation: auto;
}
The browser can then transition eligible same-origin navigations, including link navigation and history traversal. Cross-origin redirects are not eligible. The navigation remains a normal browser navigation, so links, refresh, and browser history keep their usual behavior when a transition is unavailable.
You can give one element on each page the same view-transition-name to animate it as a separate unit. For example, a product image can move from a listing card into the corresponding detail page:
/* Apply to the matching product image on both pages. */
.product-image {
view-transition-name: product-image;
}
Use a name for only one element in each captured page state. If a listing has many product images, do not apply the same name to every card; assign it only to the image for the item being opened. A repeated name in one snapshot is not a way to group elements and can prevent the intended transition.
If no named element matches between the snapshots, the root transition can still run. Start with the root effect, then add a named element only where it helps people understand the relationship between the two views.
Keep the page usable without the animation
Progressive enhancement is especially straightforward for MPA links: leave links as ordinary links and let unsupported browsers ignore the CSS rule. For an SPA, feature-detect document.startViewTransition() and call the same DOM update directly as shown above. Do not make a route depend on the animation completing.
For client-side navigation, continue to update the document title and manage focus as your app normally would. A visual transition does not replace semantic navigation or communicate a route change to assistive technology. If the destination is a new view, make sure keyboard and screen-reader users can identify that change without relying on the animation.
Check support by feature, not by API name
Same-document and cross-document support are not identical. MDN's browser-compatibility data lists document.startViewTransition() from Chrome 111, Firefox 144, and Safari 18. The @view-transition rule for cross-document navigation is newer: the current compatibility data lists Chrome 126 and Safari 18.2, but no Firefox release yet. Check the compatibility tables for the exact feature and browsers your audience uses; support in an embedded web view may also lag behind its desktop browser.
Test the fallback path as well as the animated path. In an SPA, you can temporarily call the renderer directly or test in a browser without the API. For an MPA, disable the rule and confirm that the same links and page content still work. Keep the transition short, and check both normal and reduced-motion settings.
When to use View Transitions
Use the API when a transition makes a state change easier to follow: opening an item from a list, moving between related views, or replacing a panel after a user action. It is not a general replacement for CSS animations or a reason to animate every navigation.
For a simpler entrance or interaction effect that does not connect two rendered page states, a conventional CSS animation may be easier to maintain. Our guides to CSS animation techniques and modern CSS techniques cover those alternatives. If you are designing layouts that adapt as the view changes, see the patterns in our responsive web design guide.