
Discover React’s new ViewTransition component and how it leverages concurrent features to create smooth, browser-native animations. Learn enter/exit effects, shared element transitions, list filtering, and Suspense integration with practical examples and CSS customization tips.
Aurora Scharff
September 17, 2025
View transitions are coming to React as a built-in component. Instead of complex animation libraries, you'll soon be able to create smooth, browser-native animations with minimal, declarative code.
Note: ViewTransition builds on React's concurrent features. If you're unfamiliar with useTransition and related concepts, check out React Concurrent Features: An Overview first to understand the foundation.
ViewTransition is an experimental React component that wraps the browser's View Transition API, automatically coordinating animations with React's rendering cycle. When wrapped around elements that change during a transition, it creates smooth animations between states.
The component works by creating snapshots of the old and new states, then animating between them using the browser's native View Transition API.
Here's the basic API:
import { unstable_ViewTransition as ViewTransition } from 'react';
<ViewTransition>
<Component />
</ViewTransition>
The component activates automatically when elements inside it change during a React transition, following these patterns:
ViewTransition itself is added to the DOMViewTransition itself is removed from the DOMViewTransitions coordinate between mounting/unmounting treesFor interactive demos and the latest API reference, see the React Labs post and the official docs; both include visual examples and are recommended alongside this article.
A common use case is animating elements as they appear and disappear. When a ViewTransition is added or removed during a React transition, React automatically triggers enter and exit animations.
Here's a collapsible video component example:
import { useState, startTransition } from 'react';
import { unstable_ViewTransition as ViewTransition } from 'react';
function App() {
const [showVideo, setShowVideo] = useState(false);
return (
<div>
<button onClick={() => startTransition(() => setShowVideo(!showVideo))}>
{showVideo ? '➖' : '➕'}
</button>
{showVideo && (
<ViewTransition>
<Video video={videos[0]} />
</ViewTransition>
)}
</div>
);
}
The startTransition wrapper is essential here. ViewTransition animations only activate when the state change occurs within a React transition (created by startTransition, useDeferredValue, or similar concurrent features). Without startTransition, the video would appear and disappear instantly. With it, you get a smooth fade transition that feels polished and intentional.
For more sophisticated animations, you can create shared element transitions that animate the same logical element between different locations or states. This requires giving ViewTransitions the same name prop.
Consider a video that transitions from thumbnail to fullscreen:
function VideoThumbnail({ video, onExpand }) {
return (
<ViewTransition name="video-player">
<div onClick={onExpand}>
<img src={video.thumbnail} alt={video.title} />
</div>
</ViewTransition>
);
}
function VideoFullscreen({ video, onCollapse }) {
return (
<ViewTransition name="video-player">
<div>
<button onClick={onCollapse}>✕</button>
<video src={video.url} controls />
</div>
</ViewTransition>
);
}
function App() {
const [isFullscreen, setIsFullscreen] = useState(false);
return isFullscreen ? (
<VideoFullscreen
video={videos[0]}
onCollapse={() => startTransition(() => setIsFullscreen(false))}
/>
) : (
<VideoThumbnail
video={videos[0]}
onExpand={() => startTransition(() => setIsFullscreen(true))}
/>
);
}
When transitioning between these states, React recognizes the matching names and creates a shared element transition. The video appears to smoothly morph from thumbnail size to fullscreen, maintaining visual continuity.
The name prop creates a connection between the unmounting and mounting ViewTransitions. This works even when they're in completely different component trees, as long as they have the same name and the state update occurs during the same React transition.
ViewTransition works beautifully with dynamic lists. Here's how to animate a searchable video list using useDeferredValue:
import { useState, useDeferredValue } from 'react';
import { unstable_ViewTransition as ViewTransition } from 'react';
function VideoList({ videos }) {
const [searchTerm, setSearchTerm] = useState('');
// Deferring the search term creates a transition between the immediate
// input update and the deferred filter update, activating ViewTransition
const deferredSearchTerm = useDeferredValue(searchTerm);
const filteredVideos = videos.filter(video =>
video.title.toLowerCase().includes(deferredSearchTerm.toLowerCase())
);
return (
<div>
<input
placeholder="Search videos..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
{filteredVideos.map(video => (
<ViewTransition key={video.id}>
<VideoCard video={video} />
</ViewTransition>
))}
</div>
);
}
Each video smoothly animates in or out as the filter changes, with useDeferredValue automatically creating the transition that activates the ViewTransition animations.
ViewTransition coordinates beautifully with Suspense boundaries, handling the transition from loading states to content. You can position ViewTransition in two ways for different effects:
1. Wrapping both the fallback and content (treating the transition as an update):
<ViewTransition>
<Suspense fallback={<VideoSkeleton />}>
<Video />
</Suspense>
</ViewTransition>
This creates a smooth cross-fade from skeleton to content, treating them as different states of the same logical element.
2. Wrapping the fallback and content separately (treating them as distinct elements):
<Suspense fallback={
<ViewTransition>
<VideoSkeleton />
</ViewTransition>
}>
<ViewTransition>
<Video />
</ViewTransition>
</Suspense>
This treats them as separate elements, allowing for custom enter/exit animations as the VideoSkeleton exits and Video enters.
While ViewTransition provides sensible defaults, you can customize animations using CSS View Transition classes. Instead of the default cross-fade, you can specify different animations for different activation types:
<ViewTransition
enter="slide-in"
exit="slide-out"
update="fade-slow"
>
<Content />
</ViewTransition>
Then define these animations in your global CSS using view transition pseudo-selectors:
/* Custom animation for elements exiting with class 'slide-out' */
::view-transition-old(.slide-out) {
animation: slide-out-left 300ms ease-in;
}
/* Custom animation for elements entering with class 'slide-in' */
::view-transition-new(.slide-in) {
animation: slide-in-right 300ms ease-out;
}
/* Custom animation properties for the transition group with class 'fade-slow' */
::view-transition-group(.fade-slow) {
animation-duration: 800ms;
}
@keyframes slide-out-left {
to { transform: translateX(-100%); }
}
@keyframes slide-in-right {
from { transform: translateX(100%); }
}
This approach works for any scenario, including the Suspense enter/exit pattern:
<Suspense fallback={
<ViewTransition exit="slide-down">
<VideoSkeleton />
</ViewTransition>
}>
<ViewTransition enter="slide-up">
<Video />
</ViewTransition>
</Suspense>
With corresponding CSS:
::view-transition-old(.slide-down) {
animation: slide-out-down 300ms ease-in;
}
::view-transition-new(.slide-up) {
animation: slide-in-up 300ms ease-out;
}
@keyframes slide-out-down {
to { transform: translateY(20px); opacity: 0; }
}
@keyframes slide-in-up {
from { transform: translateY(20px); opacity: 0; }
}
The skeleton slides down and fades out while the video slides up and fades in, producing a layered, dynamic effect that feels deliberate. This pattern gives you precise control over timing and motion while preserving React's simple, declarative mental model.
Many Suspense-enabled routers like Next.js will automatically trigger ViewTransition's default cross-fade when navigating between pages, since their navigation is already wrapped in transitions.
To try View Transitions in Next.js, add the following to your next.config.ts:
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
experimental: {
viewTransition: true
}
};
export default nextConfig;
ViewTransition brings browser-native animations to React without the complexity of traditional animation libraries. By wrapping elements at the right boundaries and using React's concurrent features, you get performant transitions that feel smooth and intentional.
Start with simple enter/exit animations, then explore shared elements and list filtering as your needs grow. The React documentation includes many more examples and use cases beyond what's covered here. Since this is still experimental, test thoroughly before considering it for production applications.
Sources:
Get the latest news and updates on developer certifications. Content is updated regularly, so please make sure to bookmark this page or sign up to get the latest content directly in your inbox.

Building Reusable Components with React 19 Actions
Build reusable React components with React 19 Actions using useTransition() and useOptimistic(). Learn how to track pending states, implement optimistic updates, and expose action properties for custom logic in the Next.js App Router with practical examples.
Aurora Scharff
Oct 28, 2025

Use Lighthouse to improve your Angular applications
Angular developers often focus on code structure and framework mastery—but end users care most about speed, accessibility, and visibility. This article highlights how tools like Google Chrome’s built-in Lighthouse can help you measure and improve your app’s performance, accessibility, and SEO. By running quick audits and reviewing actionable insights, developers can bridge the gap between technical excellence and real-world user experience.
Alain Chautard
Oct 24, 2025

Template Literals in JavaScript: Write Strings the Way They Were Meant to Be
Learn how template literals make JavaScript string handling modern, readable, and expressive through multi-line syntax, interpolation, and tag functions. A concise expert guide for cleaner, smarter code.
Martin Ferret
Oct 23, 2025
We can help you recruit Certified Developers for your organization or project. The team has helped many customers employ suitable resources from a pool of 100s of qualified Developers.
Let us help you get the resources you need.
