The View Transitions API: App-Like Page Changes Without the Heavy JavaScript
The View Transitions API lets browsers animate between pages and states natively. Here is how to use it on real sites, keep it accessible, and when it beats a JavaScript animation library.

For years, making a website feel like an app meant shipping a JavaScript framework or an animation library just so pages could glide into each other instead of flashing white. Browsers can now do most of that themselves. The View Transitions API animates between two states of a page, or between two separate pages, with very little code. It is one of the development trends I am actually adopting this year, and here is how I use it on client sites, and where I still reach for something heavier.
What the API actually does
The idea is simple. The browser takes a snapshot of the page as it is now, you change the page, and the browser captures the new state. It then animates between the two. By default that is a quick crossfade; with a little CSS it can be a slide, a scale, or a shared element that appears to move from one position to another.
Because the browser handles the snapshots and the animation, you do not have to keep old and new content on screen at the same time or calculate positions yourself. That fiddly part is what animation libraries used to exist for.
Same-document transitions
On a single page, or in a single-page app, you wrap the code that updates the page in document.startViewTransition(). The browser handles the rest.
function showTab(tab) {
if (!document.startViewTransition) {
renderTab(tab);
return;
}
document.startViewTransition(() => renderTab(tab));
}
The check at the top matters. Browsers without support just update the page instantly, which is exactly what they did before. Nothing breaks.
Cross-document transitions with a few lines of CSS
This is the part I find most useful for business sites. Ordinary multi-page websites — a WordPress site, a Shopify theme, a statically generated marketing site — can animate between separate pages with no JavaScript at all. Both pages opt in with this CSS:
@view-transition {
navigation: auto;
}
With that on both the old and the new page, navigations get a crossfade instead of a hard cut. It only works between pages on the same origin, so links to other sites behave as normal.
Shared elements: the product card effect
The effect people actually notice is a shared element transition: a product thumbnail that grows into the hero image on the product page, or a blog card title that slides into place as the heading. You get it by giving the matching elements the same view-transition-name on both pages.
<!-- Listing page: a unique name per card -->
<img src="/shoe-123.jpg" style="view-transition-name: product-123" alt="" />
<!-- Product page for the same item -->
<img src="/shoe-123-large.jpg" style="view-transition-name: product-123" alt="Trail running shoe" />
Each name must be unique on the page at the moment of the transition. If twenty cards share one name, the browser skips the transition entirely. Generating names from the product or post ID in your templates is the simplest way to keep them unique.
Progressive enhancement is built in
Browser support is good and still widening. Chromium browsers and Safari support both same-document and cross-document transitions, and Firefox has been adding support. Check current support tables before you depend on a particular detail — but you rarely need to depend on it, because the fallback is simply a normal page change.
That makes this one of the safest new features you can ship. Treat it as polish: the site must work and make sense without it, and it will, because that is how the API was designed.
Respect reduced motion
Some people feel genuinely unwell from large movements on screen, and many switch on their operating system's reduced motion setting for exactly that reason. A gentle crossfade is usually fine; a full-screen slide or zoom is not. Honour the setting:
@media (prefers-reduced-motion: reduce) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation: none !important;
}
}
For cross-document transitions you can go further and wrap the @view-transition rule in a prefers-reduced-motion: no-preference media query, so it only switches on for people who have not asked for less motion. This is not optional polish. It is part of building a site everyone can use, which I make the case for in why web accessibility is not optional.
Performance caveats
- Keep them short. Around 200 to 400 milliseconds feels responsive. Anything longer makes the site feel slower, not smoother.
- Cross-document transitions wait for the new page. The animation cannot start until the next page is ready to render, so a slow page is still a slow page. Fix the load time first.
- Do not animate everything. Every named element gets its own snapshot and animation. A handful is fine; dozens on a long page can stutter on cheaper phones.
- Keep the update callback fast. While it runs, the browser holds the old snapshot on screen, so heavy work inside it delays the whole change and can show up in your INP scores.
Where it beats JavaScript animation libraries
| Job | View Transitions API | JS animation library |
|---|---|---|
| Crossfade between pages | Better: no JavaScript needed | Needs client-side routing |
| Thumbnail growing into a hero image | Good, with shared names | Good, with more code |
| Tab, filter and list changes | Good | Good |
| Scroll-linked storytelling | Not designed for it | Better |
| Complex timelines and choreography | Limited | Better |
| Drag, physics and gesture-driven motion | No | Better |
For navigation and state changes, the native API is lighter, simpler and cheaper to maintain. For scroll-driven sequences and interactive motion, a library is still the right tool, and my comparison of GSAP and Framer Motion covers which one to pick. For small functional animations on buttons and inputs, plain CSS is usually enough, as I explain in micro-interactions that earn their place.
Using it in a framework
Frameworks are catching up. Astro has built-in support, and React has been developing its own view transition component. On a Next.js site the CSS opt-in does not cover client-side navigations, because those are same-document updates, so you need the framework's integration or a small wrapper around the router. If you are weighing up that stack, my notes on the Next.js App Router for business websites cover the rest. On WordPress and Shopify themes, the CSS opt-in is the easy win: add it, test it with reduced motion on, and ship.
If you are adding it to an existing site, start small. Turn on the default crossfade for page navigations, live with it for a week, and only then add one shared element on the journey that matters most, usually listing to product or card to article. Most of the perceived quality comes from that first crossfade; the rest is refinement.
Want your site to feel smoother without a heavier bundle? Tell me about your site and I will tell you where view transitions would help and where they would just be decoration.




