Astro is a modern site builder with web best practices, performance, and DX front-of-mind.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Astro's view transitions provide smooth page transitions with built-in animations, programmatic navigation, and customizable transition effects using the View Transitions API.
Enables client-side routing with view transitions.
---
import { ClientRouter } from 'astro:transitions';
---
<html>
<head>
<ClientRouter fallback="animate" />
</head>
<body>
<!-- Your content -->
</body>
</html>Props:
interface ClientRouterProps {
/**
* Fallback behavior for browsers without View Transitions API
* - 'animate': Use animation fallback
* - 'swap': Instant swap without animation
* - 'none': No client-side routing
* @default 'animate'
*/
fallback?: 'animate' | 'swap' | 'none';
}The ViewTransitions component has been deprecated in favor of ClientRouter. The migration is straightforward with no breaking changes to functionality.
Before (Deprecated):
---
import { ViewTransitions } from 'astro:transitions';
---
<html>
<head>
<ViewTransitions fallback="animate" />
</head>
</html>After (Current):
---
import { ClientRouter } from 'astro:transitions';
---
<html>
<head>
<ClientRouter fallback="animate" />
</head>
</html>What Changed:
ViewTransitions → ClientRouterfallback)astro:transitionsWhy the change: The new name better reflects that this component enables client-side routing, not just view transitions.
Creates a slide transition animation.
/**
* Creates slide transition animations
* @param options - Animation options
* @returns Directional animation configuration
*/
function slide(options?: {
duration?: string | number;
}): TransitionDirectionalAnimations;
interface TransitionDirectionalAnimations {
forwards: TransitionAnimationPair;
backwards: TransitionAnimationPair;
}
interface TransitionAnimationPair {
old: AnimationKeyframes | AnimationKeyframes[];
new: AnimationKeyframes | AnimationKeyframes[];
}---
import { slide } from 'astro:transitions';
---
<div transition:animate={slide({ duration: '0.3s' })}>
Content with slide transition
</div>Creates a fade transition animation.
/**
* Creates fade transition animations
* @param options - Animation options
* @returns Directional animation configuration
*/
function fade(options?: {
duration?: string | number;
}): TransitionDirectionalAnimations;---
import { fade } from 'astro:transitions';
---
<div transition:animate={fade({ duration: 200 })}>
Content with fade transition
</div>Creates a scoped animation name for view transitions.
/**
* Creates a scoped animation name
* @param name - Animation name
* @param options - Animation options
* @returns Scoped animation name
*/
function createAnimationScope(
name: string,
options?: AnimationOptions
): string;Programmatically navigate to a different page with view transitions.
/**
* Programmatically navigate to a URL
* Available from astro:transitions/client
* @param href - Target URL
* @param options - Navigation options
* @returns Promise resolving when navigation completes
*/
function navigate(href: string, options?: Options): Promise<void>;
interface Options {
/**
* History manipulation strategy
* - 'auto': Use push unless navigating back
* - 'push': Add new history entry
* - 'replace': Replace current history entry
* @default 'auto'
*/
history?: 'auto' | 'push' | 'replace';
/**
* Form data for POST navigation
*/
formData?: FormData;
/**
* State to store in history
*/
state?: any;
/**
* Source element for animation origin
*/
sourceElement?: Element;
/**
* Additional navigation info
*/
info?: Record<string, any>;
}import { navigate } from 'astro:transitions/client';
// Basic navigation
await navigate('/about');
// With options
await navigate('/contact', {
history: 'push',
state: { from: '/home' },
});
// POST navigation
const formData = new FormData();
formData.append('name', 'John');
await navigate('/api/submit', {
formData,
history: 'replace',
});Checks if the browser natively supports the View Transitions API.
/**
* Whether browser natively supports View Transitions API
*/
const supportsViewTransitions: boolean;import { supportsViewTransitions } from 'astro:transitions/client';
if (supportsViewTransitions) {
console.log('Native view transitions supported');
} else {
console.log('Using fallback animations');
}Gets the current fallback mode.
/**
* Gets the current fallback mode
* @returns Current fallback mode
*/
function getFallback(): Fallback;
type Fallback = 'animate' | 'swap' | 'none';Checks if transitions are enabled on the current page.
/**
* Checks if transitions are enabled on current page
* @returns True if transitions are enabled
*/
function transitionEnabledOnThisPage(): boolean;View transitions fire several lifecycle events that can be listened to.
/**
* Fired before fetching and preparing the new page
*/
const TRANSITION_BEFORE_PREPARATION: 'astro:before-preparation';
/**
* Fired after preparing but before swapping DOM
*/
const TRANSITION_AFTER_PREPARATION: 'astro:after-preparation';
/**
* Fired before swapping the DOM
*/
const TRANSITION_BEFORE_SWAP: 'astro:before-swap';
/**
* Fired after swapping the DOM
*/
const TRANSITION_AFTER_SWAP: 'astro:after-swap';
/**
* Fired when page is loaded (including first load)
*/
const TRANSITION_PAGE_LOAD: 'astro:page-load';class TransitionBeforePreparationEvent extends Event {
/**
* Function to load additional resources before transition
*/
loader: () => Promise<void>;
/**
* Target URL
*/
to: URL;
/**
* Source URL
*/
from: URL;
/**
* Navigation direction
*/
direction: Direction;
/**
* Navigation type
*/
navigationType: NavigationTypeString;
/**
* Source element that triggered navigation
*/
sourceElement: Element;
/**
* Additional navigation info
*/
info: Record<string, any>;
/**
* The new document being prepared
*/
newDocument: Document;
/**
* Abort signal for canceling transition
*/
signal: AbortSignal;
}
type Direction = 'forward' | 'back';
type NavigationTypeString = 'traverse' | 'push' | 'replace';import { TRANSITION_BEFORE_PREPARATION } from 'astro:transitions/client';
document.addEventListener(TRANSITION_BEFORE_PREPARATION, (event) => {
console.log('Navigating to:', event.to.pathname);
// Load additional resources
event.loader(async () => {
await fetch('/api/prepare');
});
});class TransitionBeforeSwapEvent extends Event {
/**
* Target URL
*/
to: URL;
/**
* Source URL
*/
from: URL;
/**
* Navigation direction
*/
direction: Direction;
/**
* Navigation type
*/
navigationType: NavigationTypeString;
/**
* Source element
*/
sourceElement: Element;
/**
* Additional navigation info
*/
info: Record<string, any>;
/**
* The new document to swap in
*/
newDocument: Document;
/**
* Abort signal
*/
signal: AbortSignal;
/**
* View transition API object
*/
readonly viewTransition: ViewTransition;
/**
* Function to perform the swap
*/
swap: () => void;
}import { TRANSITION_BEFORE_SWAP } from 'astro:transitions/client';
document.addEventListener(TRANSITION_BEFORE_SWAP, (event) => {
// Customize swap behavior
console.log('About to swap DOM');
// Access new document before swap
const newTitle = event.newDocument.title;
console.log('New page title:', newTitle);
});/**
* Type guard for TransitionBeforePreparationEvent
*/
function isTransitionBeforePreparationEvent(
value: any
): value is TransitionBeforePreparationEvent;
/**
* Type guard for TransitionBeforeSwapEvent
*/
function isTransitionBeforeSwapEvent(
value: any
): value is TransitionBeforeSwapEvent;Custom swap function implementations.
/**
* Object containing swap function implementations
*/
const swapFunctions: Record<string, (event: TransitionBeforeSwapEvent) => void>;Astro provides several transition directives for customizing animations.
<div transition:name="hero">
<!-- Element with unique transition name -->
</div>---
import { slide, fade } from 'astro:transitions';
---
<div transition:animate={slide()}>
<!-- Element with slide animation -->
</div>
<div transition:animate="fade">
<!-- Element with fade animation (built-in) -->
</div><video transition:persist>
<!-- Element state persists across navigation -->
</video>---
// src/layouts/Layout.astro
import { ClientRouter } from 'astro:transitions';
---
<html>
<head>
<ClientRouter />
<title>{title}</title>
</head>
<body>
<slot />
</body>
</html>---
import { fade } from 'astro:transitions';
---
<article transition:animate={fade({ duration: '0.3s' })}>
<h1 transition:name="title">{title}</h1>
<div class="content">
{content}
</div>
</article>import { navigate } from 'astro:transitions/client';
async function navigateWithLoading(url: string) {
showLoadingSpinner();
try {
await navigate(url);
} finally {
hideLoadingSpinner();
}
}import {
TRANSITION_BEFORE_PREPARATION,
TRANSITION_AFTER_SWAP,
TRANSITION_PAGE_LOAD,
} from 'astro:transitions/client';
// Analytics tracking
document.addEventListener(TRANSITION_PAGE_LOAD, () => {
if (typeof gtag !== 'undefined') {
gtag('config', 'GA_MEASUREMENT_ID', {
page_path: location.pathname,
});
}
});
// Loading indicator
document.addEventListener(TRANSITION_BEFORE_PREPARATION, () => {
document.body.classList.add('loading');
});
document.addEventListener(TRANSITION_AFTER_SWAP, () => {
document.body.classList.remove('loading');
});Programmatically prefetches pages for faster navigation.
/**
* Prefetches a page
* Downloads and caches page resources in advance
* @param url - URL to prefetch
* @param opts - Prefetch options
*/
function prefetch(
url: string,
opts?: PrefetchOptions
): void;
interface PrefetchOptions {
/**
* Prefetch strategy
* - 'link': Use HTML link prefetch
* - 'fetch': Use JavaScript fetch
*/
with?: 'link' | 'fetch';
/**
* When to prefetch
* - 'immediate': Prefetch immediately
* - 'eager': Prefetch as soon as possible
* - 'moderate': Prefetch when link becomes visible
* - 'conservative': Prefetch on hover/focus
*/
eagerness?: 'immediate' | 'eager' | 'moderate' | 'conservative';
/**
* Whether to ignore slow connections
* @default false
*/
ignoreSlowConnection?: boolean;
}Usage Examples:
import { prefetch } from 'astro:prefetch';
// Prefetch a single page
await prefetch('/about');
// Prefetch multiple pages
await prefetch(['/blog', '/contact', '/products']);
// Prefetch immediately
await prefetch('/dashboard', { with: 'load' });
// Prefetch even on slow connections
await prefetch('/important-page', {
with: 'load',
ignoreSlowConnection: true,
});Auto-prefetch with data attributes:
<!-- Prefetch on hover (default) -->
<a href="/about" data-astro-prefetch>About</a>
<!-- Prefetch when link is visible -->
<a href="/blog" data-astro-prefetch="visible">Blog</a>
<!-- Prefetch on page load -->
<a href="/dashboard" data-astro-prefetch="load">Dashboard</a>
<!-- Prefetch when browser is idle -->
<a href="/products" data-astro-prefetch="idle">Products</a>Configuration:
Enable prefetching globally in astro.config.mjs:
import { defineConfig } from 'astro/config';
export default defineConfig({
prefetch: {
// Prefetch all links by default
defaultStrategy: 'hover',
// Prefetch threshold for slow connections
prefetchAll: true,
},
});// Transitions and animations
import { ClientRouter, slide, fade, createAnimationScope } from 'astro:transitions';
// Client-side navigation
import {
navigate,
supportsViewTransitions,
transitionEnabledOnThisPage,
TRANSITION_BEFORE_PREPARATION,
TRANSITION_AFTER_PREPARATION,
TRANSITION_BEFORE_SWAP,
TRANSITION_AFTER_SWAP,
TRANSITION_PAGE_LOAD,
type TransitionBeforePreparationEvent,
type TransitionBeforeSwapEvent,
type Options,
} from 'astro:transitions/client';
// Prefetching
import { prefetch, type PrefetchOptions } from 'astro:prefetch';