View Transitions API in Practice: Cross-Page and Cross-Document Animated Transitions

前端工程(Updated Jun 13, 2026)

The Evolution of Page Transitions

Approach Scope Complexity Browser Native Performance
CSS transition Single component Low High
Framer Motion / GSAP SPA routing High Medium
Shared Element Transition SPA + MPA Low High
View Transitions API SPA + MPA Low High

The View Transitions API lets the browser automatically handle old→new state snapshots, animations, and cleanup. Developers only declare "when to transition" and "how to animate."


View Transitions in SPA

Basic Usage: document.startViewTransition

function navigateTo(newRoute: string) {
  if (!document.startViewTransition) {
    updateDOM(newRoute);
    return;
  }

  document.startViewTransition(() => {
    updateDOM(newRoute);
  });
}

The browser automatically:

  1. Captures the old state snapshot (::view-transition-old)
  2. Executes the callback, updating DOM
  3. Captures the new state snapshot (::view-transition-new)
  4. Runs a cross-fade animation between the two snapshots

Custom Transition Animations

Default Cross-Fade

::view-transition-old(root) {
  animation: 0.3s ease-in both fade-out;
}
::view-transition-new(root) {
  animation: 0.3s ease-out both fade-in;
}

Slide Transition Effect

::view-transition-old(root) {
  animation: 0.4s ease-in both slide-to-left;
}
::view-transition-new(root) {
  animation: 0.4s ease-out both slide-from-right;
}

@keyframes slide-to-left {
  to { transform: translateX(-100%); }
}
@keyframes slide-from-right {
  from { transform: translateX(100%); }
}

Shared Element Transitions: Cross-Component Animation

Set view-transition-name on elements, and the browser automatically performs FLIP animation between old→new positions:

.card-image {
  view-transition-name: card-img;
}
.card-title {
  view-transition-name: card-title;
}

Practice: List→Detail Hero Animation

function openDetail(cardId: string) {
  document.startViewTransition(() => {
    document.querySelector('.list-page')!.style.display = 'none';
    document.querySelector('.detail-page')!.style.display = 'block';

    document.querySelector(`#card-${cardId} img`)!
      .style.viewTransitionName = 'hero-image';
    document.querySelector('.detail-hero img')!
      .style.viewTransitionName = 'hero-image';
  });
}

The list card image → detail page hero image automatically animates with smooth scale + position transitions.


MPA Cross-Document Transitions

Chrome 111+ supports cross-document (cross-navigation) View Transitions:

Enable Cross-Document Transitions

<meta name="view-transition" content="same-origin">

One meta tag enables automatic cross-fade for same-origin navigations!

Custom CSS with Navigation Direction

@media (navigation: forward) {
  ::view-transition-old(root) { animation: slide-to-left; }
  ::view-transition-new(root) { animation: slide-from-right; }
}
@media (navigation: back) {
  ::view-transition-old(root) { animation: slide-to-right; }
  ::view-transition-new(root) { animation: slide-from-left; }
}

Advanced Techniques

Excluding Elements from Transitions

header, footer {
  view-transition-name: none;
}

Dynamic view-transition-name

document.querySelectorAll('.card').forEach((card, i) => {
  card.style.viewTransitionName = `card-${i}`;
});

Wait for Image Load Before Transition

document.startViewTransition(async () => {
  updateDOM(newRoute);
  await document.querySelector('.detail-hero img')?.decode();
});

Integration with ToolsKu


Performance Considerations

Aspect Recommendation
Snapshot size Avoid setting view-transition-name on large elements
Transition duration 200-400ms is the perceptually optimal range
Concurrent transitions Multiple shared elements animating simultaneously increases GPU load
Layout thrashing Avoid triggering layout reflows in the callback, or snapshots will be inaccurate

Common Questions

Can I update DOM asynchronously in the startViewTransition callback?

Yes. The callback supports async functions—the browser waits for the Promise to resolve before capturing the new snapshot. However, the old snapshot remains visible during the wait; excessive waiting causes a jank sensation.

Difference between cross-document and SPA transitions?

SPA uses document.startViewTransition() for manual triggering; MPA uses <meta name="view-transition"> for automatic triggering. Both share the same CSS pseudo-element and animation definition system.

How to debug View Transitions?

Chrome DevTools → Elements panel shows the ::view-transition-group pseudo-element tree. The Animation panel allows frame-by-frame transition debugging.


Summary

The View Transitions API transforms page transitions from manual FLIP calculations to browser-native snapshots + animations, dramatically reducing implementation complexity. SPA uses document.startViewTransition(), MPA uses meta tags, and shared elements use view-transition-name—three scenarios unified under one CSS pseudo-element system. This is the optimal solution for modern web application page transitions.

Try these browser-local tools — no sign-up required →

#View Transitions#页面过渡#MPA#SPA#跨文档动画