View Transitions
The best of both worlds
Section titled “The best of both worlds”A traditional multi-page site does a full browser reload on every navigation — reliable, but the screen flashes white and scroll position resets. A SPA avoids that but pays for it with a heavy JavaScript router. Astro’s view transitions give you the smooth feel of a SPA on top of a normal MPA, for a few kilobytes of script.
You opt in by adding one component, <ClientRouter />, to your page <head> — usually in a shared layout so it applies site-wide:
---import { ClientRouter } from 'astro:transitions';---<html lang="en"> <head> <title>My site</title> <ClientRouter /> </head> <body> <slot /> </body></html>With that in place, navigations between pages on your site are intercepted and handled on the client: Astro fetches the next page, swaps in its content, and animates the change — no full reload. Browsers that don’t support the underlying API simply fall back to a normal navigation, so nothing breaks.
How a navigation works
Section titled “How a navigation works”flowchart LR click["User clicks a link"] --> intercept["ClientRouter intercepts (no full reload)"] intercept --> fetch["Fetch the next page HTML"] fetch --> swap["Swap in the new content, animate"] swap --> event["Fire astro:page-load"]
Transition directives
Section titled “Transition directives”By default Astro cross-fades the page. You control animations per element with directives:
transition:name— give an element the same name on both pages so Astro morphs one into the other (a shared-element transition, like a thumbnail growing into a hero image).transition:animate— choose the animation (fade,slide, or a custom one). Import helpers fromastro:transitions.transition:persist— keep a component’s DOM and state across the navigation instead of re-rendering it. Perfect for a playing video or an audio player that shouldn’t restart.
---import { slide } from 'astro:transitions';---<!-- This heading slides between pages; the player keeps playing across nav. --><h1 transition:name="page-title" transition:animate={slide({ duration: 200 })}>Title</h1><video controls autoplay transition:name="player" transition:persist />Lifecycle events
Section titled “Lifecycle events”Because navigation no longer triggers a full page load, scripts that ran on DOMContentLoaded won’t re-run on client-side navigations. Astro fires its own events so you can re-initialize:
astro:page-load— fires after every navigation (initial load and client-side swaps). Put initialization here instead ofDOMContentLoaded.astro:after-swap— fires right after the new DOM is swapped in, before it paints (good for restoring theme/state to avoid a flash).
document.addEventListener('astro:page-load', () => { // runs on first load AND after every client-side navigation initAnalytics();});This is the most common view-transitions gotcha: an init script that works on first load but “stops working” after navigating, because it was bound to DOMContentLoaded. Move it to astro:page-load.