ShaharAmir
← Back to Blog
JavaScript3 min read

View Transitions API: Native Page Animations

Smooth transitions between pages and states — built into the browser

S
Shahar Amir

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:

javascript
1234
document.startViewTransition(() => {
// Update the DOM
document.body.innerHTML = newContent;
});

The browser automatically:

  1. Screenshots the old state
  2. Updates the DOM
  3. Screenshots the new state
  4. Animates between them

The Default Animation

By default, you get a crossfade:

css
12345678
/* 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:

css
123456789101112131415
::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:

css
12345678
/* Mark elements for individual animation */
.hero-image {
view-transition-name: hero;
}
.page-title {
view-transition-name: title;
}

css
123456789
/* 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:

html
12345
<!-- 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):

html
123
<head>
<meta name="view-transition" content="same-origin" />
</head>

Or in CSS:

css
123
@view-transition {
navigation: auto;
}

SPA Implementation

javascript
12345678
async function navigate(url) {
const response = await fetch(url);
const html = await response.text();
document.startViewTransition(() => {
document.body.innerHTML = html;
});
}

Handling the Transition

javascript
12345678910
const transition = document.startViewTransition(updateDOM);
// Wait for it
await transition.finished;
// Or get the ready state
await transition.ready;
// Skip if needed
transition.skipTransition();

Conditional Animations

Different transitions for different navigations:

javascript
1234567
function navigate(url, direction) {
document.documentElement.dataset.direction = direction;
document.startViewTransition(() => {
updateContent(url);
});
}

css
1234567
[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:

css
1234567
@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:

javascript
123456
// Next.js App Router
import { unstable_ViewTransition as ViewTransition } from 'react';
<ViewTransition>
<Component />
</ViewTransition>

Browser Support

Chrome, Edge, Opera — full support. Firefox, Safari — in development.

Feature detect:

javascript
12345
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.

#animations#transitions#browser-api

Stay Updated 📬

Get the latest tips and tutorials delivered to your inbox. No spam, unsubscribe anytime.