View Transitions
Route changes are a unique in-between state. Without visual continuity, content disappears then reappears abruptly—users lose spatial context. Where did that card go? Am I on a new page or the same page?
React's <ViewTransition> component wraps the browser's View Transitions API, enabling smooth animated transitions between routes with a declarative API.
Page-Level Transitions
Wrap page content in <ViewTransition> with enter/exit animations:
import { ViewTransition } from 'react';
export function SlideRightTransition({ children }) {
return (
<ViewTransition enter="slide-from-right" exit="slide-to-right" default="none">
{children}
</ViewTransition>
);
}
// In your page
export default function DashboardPage() {
return (
<SlideRightTransition>
<div>...</div>
</SlideRightTransition>
);
}Define the animations in CSS with @keyframes.
Shared Element Transitions
Connect elements across pages with the same name prop. When navigating, the element morphs from its position on one page to its position on the next:
// In the list view (Dashboard)
<ViewTransition name={`post-card-${post.slug}`} share="morph">
<Card>{post.title}</Card>
</ViewTransition>
// In the detail view (Post page)
<ViewTransition name={`post-card-${slug}`} share="morph">
<Card>{post.title}</Card>
</ViewTransition>The card animates smoothly from the list to its position on the detail page. Users see visual continuity—the same element moving, not disappearing and reappearing.
Directional Navigation
Create a sense of spatial hierarchy with directional transitions:
// Going "into" a detail page - slide from right
<SlideRightTransition>
<PostDetail />
</SlideRightTransition>
// Going "back" to a list - slide from left
<SlideLeftTransition>
<PostList />
</SlideLeftTransition>Forward navigation slides right; back navigation slides left. Users build a mental model of your app's structure.
Browser Support
View Transitions use the native View Transitions API. In unsupported browsers, navigation works normally without animation—progressive enhancement by default.