View Transitions API: Native Page Animations
Smooth transitions between pages and states — built into the browser
View Transitions API: Native Page Animations
Smooth page transitions used to need complex frameworks. Now the browser does it natively.
Basic Usage
Wrap any DOM change in a view transition:
document.startViewTransition(() => { // Update the DOM document.body.innerHTML = newContent;});The browser automatically:
- Screenshots the old state
- Updates the DOM
- Screenshots the new state
- Animates between them
The Default Animation
By default, you get a crossfade:
/* These are created automatically */::view-transition-old(root) { animation: fade-out 0.25s;}
::view-transition-new(root) { animation: fade-in 0.25s;}Custom Animations
Override the defaults:
::view-transition-old(root) { animation: slide-out 0.3s ease-out;}
::view-transition-new(root) { animation: slide-in 0.3s ease-out;}
@keyframes slide-out { to { transform: translateX(-100%); }}
@keyframes slide-in { from { transform: translateX(100%); }}Named Transitions
Animate specific elements differently:
/* Mark elements for individual animation */.hero-image { view-transition-name: hero;}
.page-title { view-transition-name: title;}/* Animate them separately */::view-transition-old(hero),::view-transition-new(hero) { animation-duration: 0.5s;}
::view-transition-group(title) { animation-timing-function: ease-in-out;}Shared Element Transitions
Same view-transition-name on both pages = shared element animation:
<!-- Page 1: Product List --><img class="product-thumb" style="view-transition-name: product-1" />
<!-- Page 2: Product Detail --><img class="product-hero" style="view-transition-name: product-1" />The image smoothly animates from thumbnail to hero!
With Navigation
For MPA (multi-page apps):
<head> <meta name="view-transition" content="same-origin" /></head>Or in CSS:
@view-transition { navigation: auto;}SPA Implementation
async function navigate(url) { const response = await fetch(url); const html = await response.text();
document.startViewTransition(() => { document.body.innerHTML = html; });}Handling the Transition
const transition = document.startViewTransition(updateDOM);
// Wait for itawait transition.finished;
// Or get the ready stateawait transition.ready;
// Skip if neededtransition.skipTransition();Conditional Animations
Different transitions for different navigations:
function navigate(url, direction) { document.documentElement.dataset.direction = direction; document.startViewTransition(() => { updateContent(url); });}[data-direction="back"]::view-transition-old(root) { animation: slide-right 0.3s;}
[data-direction="forward"]::view-transition-old(root) { animation: slide-left 0.3s;}Reduced Motion
Respect user preferences:
@media (prefers-reduced-motion: reduce) { ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }}Framework Support
Next.js, Nuxt, and Astro all support it:
// Next.js App Routerimport { unstable_ViewTransition as ViewTransition } from 'react';
<ViewTransition> <Component /></ViewTransition>Browser Support
Chrome, Edge, Opera — full support. Firefox, Safari — in development.
Feature detect:
if (document.startViewTransition) { document.startViewTransition(update);} else { update();}Why It Matters
- Native performance — GPU-accelerated
- Simple API — one function call
- Progressively enhanced — works without JS
- Shared elements — like native mobile apps
Page transitions are no longer a framework feature — they're a platform feature.
Stay Updated 📬
Get the latest tips and tutorials delivered to your inbox. No spam, unsubscribe anytime.