Table of Contents
- The Modern API
- The Outdated Syntax Still Floating Around
- The One-Line Setup
- Tuning the Crossfade
- Morphing an Element Across Pages
- Browser Support
The Modern API
The View Transitions API now supports cross-document navigation — the same smooth transition engine that powers single-page apps, built directly into the browser for ordinary multi-page sites. No JavaScript, no router, no framework integration. You opt in with a single CSS rule, and the browser handles the rest.
The Outdated Syntax Still Floating Around
Most tutorials about view transitions you’ll come across are already out of date. They generally fall into one of two camps:
SPA-style routers. On Astro, that’s the
<ClientRouter />component (formerly<ViewTransitions />) withtransition:animateandtransition:persistdirectives scattered through layouts. It works, but it turns a multi-page site into a client-side-routed one — JavaScript owns navigation instead of the browser, and you inherit SPA concerns like scroll restoration and scripts re-running on every route.document.startViewTransition(). That API is for same-document transitions, i.e. transitions you drive yourself from JavaScript. It’s still around, but it’s the wrong tool when you have real page navigations — and it’s no longer necessary now that the browser transitions between documents natively.
Both approaches predate native cross-document support. A good rule of thumb: if a view transitions article reaches for a router or a JavaScript API to animate a page change, it was probably written before the modern API landed.
The One-Line Setup
@view-transition {
navigation: auto;
}That’s it. Every same-origin navigation now cross-fades between the outgoing and incoming page. The outgoing page stays frozen on screen while the new page’s stylesheets finish loading, so there’s no flash of unstyled content (FOUT) — the swap happens once, smoothly, when the new page is ready.
Tuning the Crossfade
The default crossfade is perfectly usable. If you want it tuned, you can override the snapshots with your own animations:
::view-transition-old(root) {
animation: 200ms ease-in both view-fade-out;
mix-blend-mode: normal;
}
::view-transition-new(root) {
animation: 200ms ease-out both view-fade-in;
mix-blend-mode: normal;
}
@keyframes view-fade-out { to { opacity: 0; } }
@keyframes view-fade-in { from { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation: none !important;
}
}The mix-blend-mode: normal avoids the brightening the browser’s default blending can cause when pages have very different backgrounds.
Morphing an Element Across Pages
The nicest touch: give two elements the same view-transition-name and the browser will morph one into the other across the navigation. On this blog, each card thumbnail and its article banner share a name derived from the post slug:
<!-- blog card thumbnail -->
<img style="view-transition-name: banner-my-post" src="..." />
<!-- article banner -->
<img style="view-transition-name: banner-my-post" src="..." />Clicking a card makes the thumbnail smoothly scale into the article hero, so the image appears to survive the page change rather than being replaced.
Browser Support
Cross-document view transitions are supported in Chromium-based browsers (Chrome, Edge). Firefox and Safari ignore @view-transition and fall back to a normal full page load — exactly the behavior the site had before, so there’s nothing to polyfill. Combined with the reduced-motion handling above, it’s a purely additive enhancement: better where supported, unchanged where it isn’t.






