본문으로 건너뛰기 Astro View Transitions Advanced Guide — Smooth Page

Astro View Transitions Advanced Guide — Smooth Page

Astro View Transitions Advanced Guide — Smooth Page

이 글의 핵심

Separate the browser View Transitions API from Astro’s ClientRouter (client routing and transition control), and design element matching, animation, and state with transition:name, transition:animate, and transition:persist. Covers custom keyframes, fallback strategies, prefers-reduced-motion, script re-execution, and performance for real multi-page apps.

What this post is about

View Transitions are a web platform feature for maintaining visual continuity when moving from one screen to another. Astro builds on this API and fills gaps that the browser alone does not cover—client-side routing, element persistence, fallbacks, and more—via ClientRouter (the same family as the older ViewTransitions name in docs). This article goes beyond a one-line setup and connects transition:name, transition:animate, transition:persist, custom animations, fallbacks, accessibility, and performance, and production patterns for multi-page apps (MPAs) in one narrative.

Prerequisites: Familiarity with Astro layouts and pages, basic HTML/CSS animation, and concepts like browser history (pushState, etc.).


1. Splitting responsibilities: View Transitions API vs Astro

1.1 Native browser View Transitions

The View Transitions API composes animations from snapshots of the old and new views during document transitions. Chromium-based browsers have broad support first; cross-document (traditional MPA navigation) scenarios are still evolving in the standard and implementations. The core idea is: re-render the page, but present it to the user as one uninterrupted flow.

1.2 What Astro’s ClientRouter adds

Where the document alone is not enough—intercepting same-origin links, partial DOM swaps tied to history, transition:persist to keep DOM fragments, fallbacks—Astro handles this with a lightweight client router. That lets you keep an MPA’s server-rendered model while getting SPA-like feel. Script execution and re-run rules differ from a full reload, though, so treat this as application lifecycle design, not “animations only.”

The official blog discusses a future where the browser API alone may be enough; documenting why ClientRouter is needed per page helps later migration.


2. Enabling globally: where to put ClientRouter

In Astro 5, add the following to a shared <head> or layout:

---
// e.g. src/components/CommonHead.astro
import { ClientRouter } from "astro:transitions";
const { title, description } = Astro.props;
---
<meta charset="utf-8" />
<title>{title}</title>
<meta name="description" content={description} />
<ClientRouter />

This alone enables default client navigation for same-origin internal links and default transitions. You override details with transition directives.


3. transition:name: pairing elements across old and new pages

Astro infers corresponding pairs from DOM position, element type, and more. That breaks for reordered card grids, headers whose order changes responsively, or the same role expressed with different tags. Use transition:name to declare logical identity.

<!-- pages/blog/[slug].astro (previous page) -->
<article transition:name="post-hero">
  <h1>{title}</h1>
</article>
<!-- pages/blog/index.astro (next page) -->
<article transition:name="post-hero">
  <h1>Blog</h1>
</article>

Caution: Each transition:name value must appear only once per page. When rendering lists, use unique names from slug, or name only one representative thumbnail instead of every item. Colliding names produce mismatched transitions or weaker animation when the browser cannot pair elements.


4. transition:animate: built-in motion and page-level defaults

4.1 Built-in directives

You can assign animations per element:

  • fade: Crossfade-like transition close to the default (exact blend with defaults can vary by project/version).
  • initial: Stay closer to browser default styling instead of Astro’s intended crossfade.
  • slide: Slide direction flips for forward vs back (pairs well with history direction).
  • none: Disables default browser animation for that element. On the root <html>, it turns off the page-wide default fade; you can put slide only on main for a fixed chrome, sliding content pattern.
<html lang="en" transition:name="root" transition:animate="none">
  <head>...</head>
  <body>
    <header>...</header>
    <main transition:animate="slide">
      <slot />
    </main>
  </body>
</html>

4.2 Tuning with built-in animation functions

Import functions from astro:transitions and pass objects to adjust duration and easing:

---
import { fade, slide } from "astro:transitions";
---
<header transition:animate={fade({ duration: "0.4s" })}>
  ...
</header>
<section transition:animate={slide({ duration: "0.35s" })}>
  ...
</section>

Tip: Strong motion on the root often exposes layout shift and scroll restoration. Many product UIs stay header/nav near none and animate main content only.


5. Custom animations: keyframes and directional objects

For fully custom motion, define CSS @keyframes and provide Astro’s expected TransitionDirectionalAnimations, mapping old/new and forwards/backwards. Align keyframe name strings with duration, easing, and direction as needed.

---
// Layout or section component
import { ClientRouter } from "astro:transitions";

const bumpPair = {
  old: {
    name: "bump",
    duration: "0.45s",
    easing: "ease-in",
    direction: "reverse",
  },
  new: {
    name: "bump",
    duration: "0.45s",
    easing: "ease-out",
  },
};

const customTransition = {
  forwards: { old: bumpPair.old, new: bumpPair.new },
  backwards: { old: bumpPair.new, new: bumpPair.old },
};
---
<html lang="en">
  <head><ClientRouter /></head>
  <body>
    <header transition:animate={customTransition}>...</header>
  </body>
</html>

<style is:global>
  @keyframes bump {
    0% {
      opacity: 0;
      transform: translateX(24px) scale(0.98);
    }
    100% {
      opacity: 1;
      transform: translateX(0) scale(1);
    }
  }
</style>

Production tips:

  • Keep motion short: ~200–400ms often feels fast and polished.
  • For back navigation, do not blindly duplicate backwards; verify symmetry when the user goes back.
  • On low-end devices, favor transform and opacity (composition-friendly). Avoid always-on will-change—it can waste memory.

6. transition:persist and transition:persist-props

6.1 Keeping DOM, media, and islands

transition:persist keeps existing nodes instead of replacing them with the next page. It shines for video, audio widgets, complex canvases, and maps—anything where losing state hurts UX.

<video controls muted autoplay transition:persist>
  <source src="/media/intro.mp4" type="video/mp4" />
</video>

On framework islands, when the next page has the same component, the instance and internal state can carry over.

<Counter client:load transition:persist initialCount={0} />

6.2 Matching across different trees

Even when component trees differ per page, combining transition:name with transition:persist clarifies correspondence. You can also use the shorthand transition:persist="media-player" to pass a name.

6.3 transition:persist-props (Astro 4.5+)

By default, state persists but the island may re-render with the new page’s props—e.g. a header that receives the current page title should update on navigation. For rare cases where props must stay fixed (experimental UI, pinned A/B flags), add transition:persist-props. Overuse can desync the UI from the route, so define rules at the design-system level.


7. Fallbacks and browser support

ClientRouter is designed to work without the View Transitions API; use fallback to tune behavior:

  • animate (default, recommended): Simulates transitions in unsupported browsers for a similar feel.
  • swap: Instant swap with no animation—good for “old browsers get speed only.”
  • none: Closer to navigation without transition animation (verify feel per environment).
---
import { ClientRouter } from "astro:transitions";
---
<ClientRouter fallback="swap" />

Official docs recommend explicit transition:name / transition:animate for cross-browser consistency. To avoid “pretty only in Chrome,” test Safari and Firefox on real devices for flicker, scroll, and focus.


8. Performance

8.1 Animation cost

  • Properties that drive layout (width, height, top, left, …) tend to cause reflow. Prefer transform / opacity when possible.
  • Heavy motion on many elements at once stacks main-thread and compositing cost. Tier motion—e.g. one hero + body fade.
  • If images/fonts are still loading when a transition starts, layout jumps collide with animation. Combine with fixed dimensions, placeholders, and font-display strategy.

8.2 Network and cache

The client router fetches and parses the next HTML. Document size, CSS/JS chunk count, and server TTFB directly affect perceived transition speed. Duplicated inline styles and huge head blocks that grow per page increase swap cost.

8.3 Accessibility: prefers-reduced-motion

Astro documents respect for system reduced-motion settings. With custom CSS, provide a safe fallback:

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation-duration: 0.001ms !important;
  }
}

Some teams prefer a minimal fade instead of fully disabling motion, per product policy.


9. Production MPA transition patterns

For checkout, auth, or pages that need third-party script reinjection, turn off client routing:

<a href="/checkout/confirm" data-astro-reload>Confirm payment</a>

9.2 History control: data-astro-history

Use push, replace, or auto to control history stack behavior—useful for mobile web flows like “don’t stack the confirmation screen.”

9.3 Programmatic navigation: navigate()

From selects, search results, or shortcuts, use navigate from astro:transitions/client. Do not pipe raw user input into URLs—the docs warn about open redirects and XSS. Validate with an allowlist of paths.

import { navigate } from "astro:transitions/client";

const allowed = ["/dashboard", "/settings", "/profile"] as const;

export function go(path: string) {
  if ((allowed as readonly string[]).includes(path)) {
    navigate(path);
  }
}

9.4 Forms and transitions (Astro 4+)

GET/POST forms integrate with the router. Default POST aligns with multipart/form-data; for traditional application/x-www-form-urlencoded, set enctype explicitly. For a full reload on submit, put data-astro-reload on the form.

9.5 Scripts and lifecycle events

With client routing, bundled module scripts run once. Re-initialize menus, analytics, and form bindings on documented events like astro:page-load and astro:after-swap. Test focus and scroll restoration after transitions from a WCAG perspective.


10. Troubleshooting checklist

SymptomWhat to check
Elements don’t “connect”transition:name collisions; conditional render so one page lacks the element
Animation only looks wrongRoot transition:animate="none" vs child motion; missing custom keyframes
Back navigation feels offbackwards definition, slide direction, scroll position
JS doesn’t runModule script single-run rule; need event-driven re-init
Flicker in some browsersfallback value; explicit transition:animate

11. Summary

The View Transitions API is more than “pretty transitions”—it helps preserve user attention and context. Astro’s ClientRouter adds MPA-friendly client routing and transition:* directives for real requirements. Production-grade smooth transitions need transition:name for pairing, transition:animate for tiered motion, transition:persist for state, plus fallback, reduced motion, performance, and script lifetime considered together.


References


자주 묻는 질문 (FAQ)

Q. 이 내용을 실무에서 언제 쓰나요?

A. Astro ClientRouter and View Transitions: transition:name, animate, persist, custom keyframes, fallbacks, accessibility, … 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.

Q. 선행으로 읽으면 좋은 글은?

A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.

Q. 더 깊이 공부하려면?

A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.


이 글에서 다루는 키워드 (관련 검색어)

Astro, View Transitions, Animation, Performance, UX 등으로 검색하시면 이 글이 도움이 됩니다.