Cross-document View Transitions: the CSS that replaces the router
Auditing a static Astro project to remove the ClientRouter component — it had no JS state to preserve between pages. One CSS rule in the global stylesheet is all it takes:
@view-transition {
navigation: auto;
}That’s it. The browser intercepts navigation, captures the current document’s visual state, loads the next page in background, and runs a native cross-fade. No JavaScript. No router. No extra config. All modern browsers support it natively; Baseline 2026.
Shared element transitions between pages#
The real power isn’t the global fade — it’s morphing specific elements across documents. Give the same view-transition-name to matching elements in both pages, and the browser animates the transition between them automatically:
/* Post listing page */
.post-card img {
view-transition-name: post-hero;
}
/_Post detail page_/
.hero-image {
view-transition-name: post-hero;
}
The name acts as an anchor between the two documents. The browser finds the pair and builds the animation. Zero JavaScript.
When ClientRouter is still the right call#
Pure CSS vs ClientRouter
Pure CSS @view-transition is enough when...
- Your site is a static MPA with no shared JS state between pages.
- You only need navigation animations (fade, image morph).
- You want zero JavaScript in the layout shell.
Use ClientRouter when...
- You need to preserve audio/video players across page changes.
- You use reactive stores (nanostores, Zustand) that shouldn't reset.
- You need programmatic control over when and how the transition starts.
If you were using ClientRouter purely for enter/exit animations, this CSS replaces it at zero cost. The full trade-off breakdown is in the ClientRouter in Astro 6 TIL. If transitions don’t show up on mobile, the debugging View Transitions on mobile TIL covers the full diagnostic system.