DEV Community

Alex Spinov
Alex Spinov

Posted on

Astro View Transitions Have Free Page Animations — Here's How to Add Them in 5 Minutes

Full-page navigations feel jarring. Astro View Transitions make your MPA feel like an SPA — with smooth animations and zero client-side routing.

What Are View Transitions?

View Transitions are a browser API that animates between page navigations. Astro integrates them so multi-page apps get SPA-like transitions with no JavaScript framework.

Quick Setup

---
// src/layouts/Layout.astro
import { ViewTransitions } from 'astro:transitions';
---
<html>
  <head>
    <ViewTransitions />
  </head>
  <body>
    <slot />
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

That is it. Every page navigation now has a smooth crossfade animation.

Built-in Animations

---
import { fade, slide } from 'astro:transitions';
---

<!-- Fade (default) -->
<div transition:animate="fade">
  Content fades in/out
</div>

<!-- Slide -->
<div transition:animate={slide({ duration: '0.3s' })}>
  Content slides in from the side
</div>

<!-- None (instant) -->
<div transition:animate="none">
  No animation
</div>

<!-- Initial (only animates on first load) -->
<div transition:animate="initial">
  Animates only once
</div>
Enter fullscreen mode Exit fullscreen mode

Persistent Elements

<!-- Header stays in place across navigations -->
<header transition:persist>
  <nav>...</nav>
</header>

<!-- Video keeps playing -->
<video transition:persist autoplay>
  <source src="/video.mp4" />
</video>

<!-- Audio player persists -->
<div transition:persist id="player">
  <AudioPlayer />
</div>
Enter fullscreen mode Exit fullscreen mode

Named Transitions (Hero Animations)

<!-- Blog list page -->
{posts.map(post => (
  <a href={`/blog/${post.slug}`}>
    <img
      src={post.cover}
      transition:name={`hero-${post.slug}`}
    />
    <h2 transition:name={`title-${post.slug}`}>{post.title}</h2>
  </a>
))}

<!-- Blog detail page -->
<img
  src={post.cover}
  transition:name={`hero-${post.slug}`}
/>
<h1 transition:name={`title-${post.slug}`}>{post.title}</h1>
Enter fullscreen mode Exit fullscreen mode

The image and title smoothly morph from the list page to the detail page.

Custom Animations

---
import { fade } from 'astro:transitions';
---

<style>
  @keyframes slideUp {
    from { opacity: 0; transform: translateY(20px); }
    to { opacity: 1; transform: translateY(0); }
  }
  @keyframes slideDown {
    from { opacity: 1; transform: translateY(0); }
    to { opacity: 0; transform: translateY(-20px); }
  }
</style>

<div
  transition:animate={{
    old: { name: 'slideDown', duration: '0.3s' },
    new: { name: 'slideUp', duration: '0.3s', delay: '0.1s' },
  }}
>
  Custom animated content
</div>
Enter fullscreen mode Exit fullscreen mode

Lifecycle Events

<script>
  document.addEventListener('astro:before-preparation', (e) => {
    // Before new page is fetched
    console.log('Navigating to:', e.to);
  });

  document.addEventListener('astro:after-swap', () => {
    // After DOM is swapped, before animations
    // Re-initialize scripts here
  });

  document.addEventListener('astro:page-load', () => {
    // Page fully loaded and animated
    // This runs on every navigation, including initial load
  });
</script>
Enter fullscreen mode Exit fullscreen mode

Fallback for Older Browsers

Astro's View Transitions automatically fall back to standard navigation in browsers that do not support the View Transitions API. No broken experiences.

View Transitions vs Client-Side Routing

Feature Astro View Transitions React Router Next.js
JS Bundle 0KB 50KB+ 80KB+
Setup 1 line Routes config Pages dir
SEO Perfect (MPA) Needs SSR Good
Animations Built-in Manual Manual
Browser Back Native Simulated Simulated
Persistent Elements Built-in Complex Complex

Building content sites with dynamic data? Check out my Apify actors — extract content from any website for your Astro site. For custom solutions, email spinov001@gmail.com.

Top comments (0)