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>
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>
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>
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>
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>
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>
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)