Rike Pool

When browsers grew up

The View Transitions API is a browser mechanism that allows the DOM to animate fluidly between states. With the release of Firefox 144 this month, it is now supported by all major browsers. This update enables developers to build multi-page applications (MPAs) that feel like Single-Page Applications (SPAs) without the heavy JavaScript architecture.

In short, we can now instruct the browser to snapshot the old page, load the new one, and morph the pixels between them. You get the user experience of an app with the simplicity of static HTML.

I have updated this site to use it. It solves the long-standing trade-off between “feeling fast” (SPAs) and “being simple” (MPAs).

The problem: Jarring page loads

Traditional web navigation is abrupt. When a user clicks a link, the browser destroys the current document and paints a white screen before rendering the new one. This “hard refresh” breaks the user’s mental model of where they are in the application.

SPAs solved this by hijacking navigation. They use JavaScript to fetch content and swap it into the DOM, allowing for smooth animations. But this introduces significant complexity: you must manage client-side routing, memory leaks, and history state.

The View Transitions API gives us the best of both worlds. We keep the simple architecture of a multi-page site but get the fluid transitions previously reserved for heavy JavaScript frameworks. This aligns with the concept of softening the stack-using the platform’s native capabilities to reduce our reliance on complex tooling.

How it works: Naming and morphing

The browser handles the heavy lifting. During a navigation event, it rasterizes the current page into a pseudo-element, loads the new page, and rasterizes that as well. It then runs a CSS animation to transition between the two snapshots.

Your only job is to tag elements that should persist across pages using the view-transition-name CSS property.

/* Old page: product list */
.thumbnail {
  view-transition-name: product-image;
}

/* New page: product detail */
.hero-image {
  view-transition-name: product-image;
}

When the browser detects two elements with the same view-transition-name during a transition, it automatically calculates the transform needed to morph the first into the second. It handles position, size, and aspect ratio changes on the compositor thread, ensuring 60fps performance.

Sanity check

Before you start, verify that the API is active in your environment. You can run this simple check in your browser’s console:

if ('startViewTransition' in document) {
  console.log('View Transitions API is supported.');
} else {
  console.log('View Transitions API is NOT supported.');
}

If you are testing cross-document transitions (navigating between two different HTML files), ensure you have the correct meta tags or CSS headers enabled, as this requires stricter opt-ins than Single-Page Application transitions.

The Astro implementation

Frameworks like Astro have already wrapped this API to make it “drop-in” ready. In Astro 5.0, the router handles the fallback logic for older browsers and normalizes the behavior across the stack.

We use the ClientRouter component (formerly ViewTransitions):

---
// layouts/Layout.astro
import { ClientRouter } from 'astro:transitions';
---

<html>
  <head>
    <ClientRouter />
  </head>
  <body>
    <slot />
  </body>
</html>

By adding this component to your head, Astro intercepts link clicks and manages the transition lifecycle. It effectively turns a static site into a pseudo-SPA. This is particularly powerful when combined with interactive islands, as seen in our MDX Showcase, allowing for rich, app-like experiences on static content.

Things to watch out for

While the API is baseline, implementation details matter.

  1. Browser support: Native cross-document transitions are now in Firefox 144, Chrome 111+, and Safari 18+. If you need to support older versions, use a framework like Astro that includes a software fallback.
  2. Accessibility: Motion triggers vestibular disorders for some users. You must respect the prefers-reduced-motion media query.
  3. Performance: Do not animate everything. animating huge elements (like the entire body) can be expensive. Stick to key focal points like images or headers.

Here is the correct way to handle reduced motion:

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

Where this is going

For a decade, we broke the web’s native navigation model to make it feel better. We are now reversing that trend.

We can build simple, performant HTML sites that feel native. This reduces our dependency on client-side JavaScript and lets the browser engine do what it was designed to do. We will look back at 2025 as the year the “app-like” feel became a standard HTML feature rather than a React dependency.

FAQ

Q: Do I need a framework to use View Transitions?

A: No. You can use document.startViewTransition() in vanilla JavaScript for Single-Page Apps. However, for Multi-Page Apps (traditional websites), you currently need a small script or a framework like Astro to trigger the transition on link clicks until the declarative HTML API is fully standardized.

Q: Does this hurt SEO?

A: No. Unlike SPAs which often struggle with indexing, View Transitions on a multi-page app (like Astro) still ship standard HTML files for every route. Search engines see a normal website; users see a fluid app.

Q: How is this different from CSS transitions?

A: CSS transitions animate properties on an element that stays in the DOM. The View Transitions API animates between two different DOM states (even if the elements are completely replaced). It snapshots the “before” and “after” states and cross-fades or morphs them.

Q: Is ClientRouter the same as the old ViewTransitions component in Astro?

A: Yes. Astro 5.0 renamed the <ViewTransitions /> component to <ClientRouter /> to better reflect its role. It handles client-side routing logic in addition to just the visual transitions.