Privacy Policy© 2026 DEV BAK - TECH BLOG. All rights reserved.
DEV BAK - TECH BLOG
frontend

View Transitions API — Production Page Transition Animations Without Libraries, After Achieving Baseline 2025

Honestly, my first reaction when I saw this API was "this actually works?" The idea of implementing app-like page transitions with just a few lines of CSS and a single JavaScript method — no Framer Motion, no GSAP — seemed too good to be true. But after using it in practice, it really does work. Because the browser handles the before-and-after snapshots at the compositor layer, the transform·opacity-based animation segments produce surprisingly smooth results.

In October 2025, with the release of Firefox 144, all four major browsers — Chrome, Edge, Safari, and Firefox — now support document.startViewTransition(). This marks the point where it can be used in production without polyfills or "Chrome only" comments. That's exactly why I'm writing this. If you're looking to introduce transition animations to an existing MPA for the first time, or if you want to drop a library dependency like Framer Motion, now is exactly the right time.

This article covers everything from how the API works, to framework-specific integration patterns, to the pitfalls you're likely to fall into in real-world use. Whether you're working in a SPA, MPA, Next.js, Astro, or SvelteKit environment, everything here is ready to apply directly.


Core Concepts

Baseline Newly Available: A status jointly defined by the W3C and browser vendors. It means the feature works in the latest versions of Chrome, Edge, Safari, and Firefox — the official milestone for "safe to use." The View Transitions API (same-document) reached this status in October 2025.

The Three Phases the Browser Uses to Handle a Transition

The mechanism is simpler than you might think. Rather than directly manipulating the DOM like complex animation libraries, the browser takes a snapshot of the state "before" and "after," then fills in the gap.

  1. document.startViewTransition(callback) is called → the browser takes a snapshot of the current screen
  2. DOM changes are performed inside callback
  3. The browser transitions from the old snapshot → new DOM using either the default crossfade or a custom CSS animation

The default is a crossfade, and you can customize it with CSS however you like.

SPA Mode vs. MPA Mode

The API is divided into two primary modes.

Same-document (SPA mode): You call document.startViewTransition() directly from JavaScript. As of October 2025, it is fully supported in Chrome 111+, Edge 111+, Safari 18+, and Firefox 144+. It is Baseline Newly Available and suitable for production use.

Cross-document (MPA mode): Applies transition animations between two pages of the same origin using only the CSS @view-transition at-rule. No JavaScript required at all. Supported in Chrome 126+, Edge 126+, and Safari 18.2+, but Firefox does not yet support it.

css
/* Just add these two lines to both pages */
@view-transition {
  navigation: auto;
}

Shared Element Transitions

I didn't believe this could be this easy at first, but this is where the real magic of the View Transitions API lies. By assigning a name to a specific element with the view-transition-name CSS property, that element moves independently during a page transition. This is the effect where an image smoothly flies from a product card to the product detail page.

css
/* Card image on the list page */
.product-card img {
  view-transition-name: product-hero;
}
 
/* Hero image on the detail page */
.product-detail .hero {
  view-transition-name: product-hero;
  /* Prevents stretching when the image aspect ratio differs */
  object-fit: cover;
}
 
/* Customize the transition animation */
::view-transition-old(product-hero),
::view-transition-new(product-hero) {
  animation-duration: 0.4s;
  animation-timing-function: ease-in-out;
}

When elements with the same view-transition-name are found on both pages, the browser automatically interpolates between them — position, size, and shape included.

Good to Know — Level 2 Specification

The Level 2 specification currently under active development by the W3C contains features that address pain points felt in real-world use. These aren't features you can use right now, but it's helpful to understand where the API is headed.

view-transition-class: While view-transition-name is a unique ID per element, view-transition-class assigns a shared class to multiple elements so the same animation style can be applied to all of them at once.

Automatic name generation for repeated elements: A feature is being discussed that would have the browser automatically generate internal names for repeated elements like card lists. Values such as match-element and auto are under consideration, though nothing is finalized yet. For now, you must manually assign unique names to each dynamically generated list item.

:active-view-transition-type() pseudo-class: Allows you to conditionally apply different CSS animations depending on the transition type. The example below includes both a version using CSS nesting syntax (the & selector) and an expanded version.

css
/* Using CSS nesting syntax */
:active-view-transition-type(slide-left) {
  &::view-transition-old(root) { animation-name: slide-out-left; }
  &::view-transition-new(root) { animation-name: slide-in-right; }
}
 
/* Same effect without CSS nesting */
:active-view-transition-type(slide-left)::view-transition-old(root) {
  animation-name: slide-out-left;
}
:active-view-transition-type(slide-left)::view-transition-new(root) {
  animation-name: slide-in-right;
}

Practical Application

Page Transition Animations in an MPA Without JavaScript

This is the fastest way to see results. In Astro or a traditional multi-page app, you don't need to touch a single line of JavaScript.

css
/* Add to <head> or global CSS on every page */
@view-transition {
  navigation: auto;
}
 
/* Replace the default crossfade with a slide transition */
@keyframes slide-in-from-right {
  from { transform: translateX(30px); opacity: 0; }
  to   { transform: translateX(0);    opacity: 1; }
}
 
@keyframes slide-out-to-left {
  from { transform: translateX(0);     opacity: 1; }
  to   { transform: translateX(-30px); opacity: 0; }
}
 
::view-transition-old(root) {
  animation: 300ms ease-in-out slide-out-to-left;
}
 
::view-transition-new(root) {
  animation: 300ms ease-in-out slide-in-from-right;
}
Component Description
@view-transition { navigation: auto; } Automatically activates transitions on same-origin page navigation
::view-transition-old(root) Animation for the departing page (previous snapshot)
::view-transition-new(root) Animation for the arriving page (new DOM)
root Targets the entire page. Can be replaced with a specific element name

Firefox users will experience normal page navigation without animation. The functionality itself works correctly, so this is fine from a Progressive Enhancement perspective.

Framework-Specific Integration Patterns

Here are the most commonly used patterns. Integration approaches differ quite a bit between frameworks.

Astro: Declare <ViewTransitions /> once in your layout component and you're done. This is the most natural integration — you keep the MPA structure while getting SPA-like transitions.

astro
---
import { ViewTransitions } from 'astro:transitions';
---
<html>
  <head>
    <ViewTransitions />
  </head>
  <body>
    <slot />
  </body>
</html>

Next.js (App Router): Adding a flag to next.config.js automatically triggers startViewTransition on <Link> clicks. Supported in Next.js 15+. Pairing it with React 19.2's <ViewTransition> component allows for finer-grained control.

javascript
// next.config.js (Next.js 15+)
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    viewTransition: true,
  },
};
 
export default nextConfig;

The key with the <ViewTransition> component is that the same name must be used on both the list and detail sides for the shared element transition to connect.

jsx
// List page
import { ViewTransition } from 'react';
 
function ProductCard({ product }) {
  return (
    <ViewTransition name={`product-${product.id}`}>
      <img src={product.image} alt={product.name} />
    </ViewTransition>
  );
}
 
// Detail page — the name must match for the two elements to connect as one transition
function ProductDetail({ product }) {
  return (
    <ViewTransition name={`product-${product.id}`}>
      <img src={product.image} alt={product.name} className="hero" />
    </ViewTransition>
  );
}

SvelteKit: There's no official built-in support, but a pattern using the onNavigate hook is well-established in the community. The reason for returning a Promise is to explicitly signal to the browser when the transition is complete. Without this structure, SvelteKit has no way of knowing when to finish the navigation.

javascript
// +layout.svelte or +layout.js
import { onNavigate } from '$app/navigation';
 
onNavigate((navigation) => {
  // Pass through silently in unsupported browsers
  if (!document.startViewTransition) return;
 
  // Return a Promise so the browser knows when the transition is complete
  return new Promise((resolve) => {
    document.startViewTransition(async () => {
      resolve();
      await navigation.complete;
    });
  });
});

Motion Handling with Accessibility in Mind

Skipping this will almost certainly get flagged in a real-world project. The browser does not automatically respect prefers-reduced-motion, so developers must handle it manually. It can cause discomfort for users with vestibular disorders or motion sensitivity, and it's a commonly cited issue in accessibility audits.

css
/* Recommended to include in global CSS from the start */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

Pros and Cons

Advantages

Item Details
Eliminates library dependencies Works with native CSS animations alone — no Framer Motion or GSAP needed
Compositor layer processing transform·opacity transitions are handled by the GPU for smooth animation
Progressive Enhancement Unsupported browsers navigate normally. Can branch on document.startViewTransition existence
MPA support Apply page transition animations via the @view-transition at-rule without a client-side router
No layout shift The snapshot is already taken before DOM changes, so layout doesn't flicker during transitions
Baseline achieved Supported in the latest versions of all four major browsers since October 2025 (same-document)

Of these, the one that makes the biggest practical difference is MPA support. Not needing to add a client-side router makes a substantial difference in Astro or existing server-rendered app projects.

Compositor Layer: A layer handled directly by the GPU in the browser's rendering pipeline. When properties like transform and opacity are processed at this layer, they don't touch CPU layout calculations, resulting in much smoother performance. The View Transitions API leverages these properties by default when transitioning between snapshots, which is why it has a performance advantage.

Disadvantages and Caveats

Item Details Mitigation
No concurrent transitions Only one transition can run per document. If a new transition fires during an existing one, the previous one is skipped Consider a queue structure that triggers the next transition after the current one completes
Duplicate view-transition-name If two or more elements share the same name on the same screen, the entire transition is skipped Assign unique ID-based names to dynamic lists (improvement planned for Level 2)
Callback blocking Users see the previous page frozen while the callback hasn't resolved Avoid heavy data fetching inside the callback. Load data first, then start the transition
Accessibility issues Screen readers may announce both old and new DOM content simultaneously; focus can become confused during transitions Manage ARIA live regions and explicitly move focus after the transition completes
prefers-reduced-motion not handled automatically The browser does not automatically apply reduced-motion settings Must manually write @media (prefers-reduced-motion: reduce) in CSS
Firefox cross-document not supported Firefox falls back to normal navigation when @view-transition is used in an MPA Handle via Progressive Enhancement. Under discussion for Interop 2026

The Most Common Mistakes in Real-World Use

  1. Duplicate view-transition-name on dynamic lists: If you don't generate names based on an ID — like product-1, product-2 — the moment two elements with the same name appear, the entire transition silently skips. This is the kind of thing that can have you scratching your head for a while trying to figure out why it's not working.

  2. Calling fetch inside the callback: While the data is being fetched, the previous page is frozen and shown to the user as-is. It's better to finish loading the data first, then start the transition.

  3. Omitting prefers-reduced-motion handling: Building the habit of including this in your global CSS from the start means you never have to think about it later. It's also a frequently cited issue in accessibility audits.


Closing Thoughts

The View Transitions API is no longer an "experimental feature" — it is a proven, browser-native tool ready for production use without any library. So where can you start right now?

  1. If you have an MPA project: Try adding the two lines @view-transition { navigation: auto; } to your global CSS. You'll immediately see a default crossfade applied to every page navigation, with no additional JavaScript required.

  2. If you're on a SPA or framework project: For Next.js, start by adding experimental: { viewTransition: true } to next.config.js. For Astro, try adding the <ViewTransitions /> component to your layout. For SvelteKit, paste the onNavigate pattern above into +layout.svelte.

  3. If you want the most impressive effect: If you have a repeating card UI — like a product list leading to a detail view — assign the same view-transition-name to the card image and the detail page hero image. The moment it first works, you'll understand the excitement firsthand.


References

  • View Transition API - MDN Web Docs — Basic API reference; a good starting point when first approaching this
  • Using the View Transition API - MDN — Step-by-step implementation details
  • What's new in view transitions (2025 update) - Chrome for Developers — Summary of 2025 changes from the Chrome team
  • Smooth transitions with the View Transition API - Chrome for Developers — Official implementation guide with abundant examples
  • Misconceptions about view transitions - Chrome for Developers — Common misconceptions; useful when making the case to your team
  • CSS View Transitions Module Level 2 - W3C Draft — Level 2 specification draft; check syntax before it's finalized
  • React Labs: View Transitions, Activity, and more - React Official Blog — Original React <ViewTransition> announcement
  • React <ViewTransition> Official Docs — Component API reference
  • next.config.js: viewTransition - Next.js Official Docs — Next.js 15+ integration setup
  • View transitions - Astro Official Docs — Advanced Astro patterns including persist and animate directives
  • Animating Page and View Transitions with Accessibility in Mind — In-depth accessibility handling including ARIA and focus management
  • Can I use: View Transitions API — Real-time current browser support status
#ViewTransitionsAPI#CSS#JavaScript#NextJS#Astro#SvelteKit#MPA#SPA#점진적향상#웹애니메이션
Share

Table of Contents

Core ConceptsThe Three Phases the Browser Uses to Handle a TransitionSPA Mode vs. MPA ModeShared Element TransitionsGood to Know — Level 2 SpecificationPractical ApplicationPage Transition Animations in an MPA Without JavaScriptFramework-Specific Integration PatternsMotion Handling with Accessibility in MindPros and ConsAdvantagesDisadvantages and CaveatsThe Most Common Mistakes in Real-World UseClosing ThoughtsReferences

Recommended Posts

We Migrated from Webpack to Rsbuild and Production Builds Got 74% Faster — The Migration Reality and Rspack Pitfalls
frontend

We Migrated from Webpack to Rsbuild and Production Builds Got 74% Faster — The Migration Reality and Rspack Pitfalls

Honestly, my first reaction was "another new build tool?" — same as when Vite came out, same as when Turbopack was announced. But after seeing the case from Ala...

June 23, 202619 min read
Fixing Micro Frontend Style Conflicts with CSS @layer | Cascade Layers Isolation Strategy
frontend

Fixing Micro Frontend Style Conflicts with CSS @layer | Cascade Layers Isolation Strategy

When I first worked on a Micro Frontend (MFE) project, I remember being quite surprised to see a button style deployed by Team A showing up altered on Team B's ...

June 23, 202622 min read
When Linting Gets 62x Faster, Your Development Habits Change — Migrating from ESLint to Oxlint in a Vite Project
frontend

When Linting Gets 62x Faster, Your Development Habits Change — Migrating from ESLint to Oxlint in a Vite Project

Have you ever felt that your linter is slow? I did too. I used to think waiting nearly 30 seconds every time I ran on a mid-sized React project was just normal...

June 23, 202619 min read
HTMX 4.0 Server Rendering Patterns: Architecture Choices for Building Interactive Web Apps Without Client State
frontend

HTMX 4.0 Server Rendering Patterns: Architecture Choices for Building Interactive Web Apps Without Client State

When I first learned React, I honestly buried this question in the back of my mind: "Does clicking a single button really require this much code?" State managem...

June 7, 202622 min read
How to Reduce TTFB from 350ms to 60ms with Next.js RSC + Streaming
frontend

How to Reduce TTFB from 350ms to 60ms with Next.js RSC + Streaming

While reviewing the Core Web Vitals of a production service, I once discovered pages where TTFB was well over 400ms. Each DB query was individually fast, so I w...

June 7, 202619 min read
Speed Up `next build` 10× with Next.js Turbopack Build Cache — Pitfalls of Experimental Flags and CI Integration Strategies
frontend

Speed Up `next build` 10× with Next.js Turbopack Build Cache — Pitfalls of Experimental Flags and CI Integration Strategies

When in CI starts exceeding three minutes, the stress quietly accumulates. The time you spend waiting in front of the deployment pipeline every time you open a...

May 30, 202616 min read