Documentation Index
Fetch the complete documentation index at: https://mintlify.com/remix-run/react-router/llms.txt
Use this file to discover all available pages before exploring further.
useViewTransitionState
Returns true when there is an active View Transition to the specified location. This can be used to apply finer-grained styles to elements to further customize the view transition.
import { useViewTransitionState } from "react-router";
function ImagePreview({ src }: { src: string }) {
const isTransitioning = useViewTransitionState("/images/123");
return (
<img
src={src}
style={{
viewTransitionName: isTransitioning ? "image-expand" : "",
}}
/>
);
}
Parameters
The location to check for an active View Transition. Can be a string path or a partial Path object with pathname, search, and hash.
relative
"route" | "path"
default:"\"route\""
The relative routing type to use when resolving the to location:
"route" (default) - Relative to the route hierarchy
"path" - Relative to the URL path segments
Return Value
true if there is an active View Transition to the specified location, otherwise false.
Type Declaration
declare function useViewTransitionState(
to: To,
options?: { relative?: "route" | "path" }
): boolean;
type To = string | Partial<Path>;
interface Path {
pathname: string;
search: string;
hash: string;
}
Usage Examples
Image Gallery Transitions
import { useViewTransitionState, Link } from "react-router";
function ImageGallery({ images }: { images: Image[] }) {
return (
<div className="gallery">
{images.map((image) => (
<ImageThumbnail key={image.id} image={image} />
))}
</div>
);
}
function ImageThumbnail({ image }: { image: Image }) {
const to = `/images/${image.id}`;
const isTransitioning = useViewTransitionState(to);
return (
<Link to={to} viewTransition>
<img
src={image.thumbnailUrl}
alt={image.alt}
style={{
viewTransitionName: isTransitioning ? `image-${image.id}` : "",
}}
/>
</Link>
);
}
Animated List Items
import { useViewTransitionState, Link } from "react-router";
function ProductList({ products }: { products: Product[] }) {
return (
<ul>
{products.map((product) => (
<ProductItem key={product.id} product={product} />
))}
</ul>
);
}
function ProductItem({ product }: { product: Product }) {
const to = `/products/${product.id}`;
const isTransitioning = useViewTransitionState(to);
return (
<li
style={{
viewTransitionName: isTransitioning ? `product-${product.id}` : "",
}}
>
<Link to={to} viewTransition>
<h3>{product.name}</h3>
<p>{product.description}</p>
</Link>
</li>
);
}
Conditional Transition Styles
import { useViewTransitionState } from "react-router";
function Card({ id, title }: { id: string; title: string }) {
const isExpanding = useViewTransitionState(`/cards/${id}`);
const isCollapsing = useViewTransitionState("/cards");
return (
<div
className="card"
style={{
viewTransitionName: isExpanding || isCollapsing ? `card-${id}` : "",
}}
>
<h2>{title}</h2>
</div>
);
}
Multiple Transition Elements
import { useViewTransitionState, Link } from "react-router";
function ArticlePreview({ article }: { article: Article }) {
const to = `/articles/${article.slug}`;
const isTransitioning = useViewTransitionState(to);
return (
<article>
<Link to={to} viewTransition>
<img
src={article.coverImage}
style={{
viewTransitionName: isTransitioning
? `article-image-${article.slug}`
: "",
}}
/>
<h2
style={{
viewTransitionName: isTransitioning
? `article-title-${article.slug}`
: "",
}}
>
{article.title}
</h2>
</Link>
</article>
);
}
Common Patterns
Shared Element Transitions
import { useViewTransitionState, useParams, Link } from "react-router";
// List view
function MovieList({ movies }: { movies: Movie[] }) {
return (
<div>
{movies.map((movie) => (
<MovieCard key={movie.id} movie={movie} />
))}
</div>
);
}
function MovieCard({ movie }: { movie: Movie }) {
const to = `/movies/${movie.id}`;
const isTransitioning = useViewTransitionState(to);
return (
<Link to={to} viewTransition>
<img
src={movie.poster}
style={{
viewTransitionName: isTransitioning ? `movie-poster-${movie.id}` : "",
}}
/>
</Link>
);
}
// Detail view
function MovieDetail() {
const { id } = useParams();
const isTransitioning = useViewTransitionState("/movies");
return (
<div>
<img
src={movie.poster}
style={{
viewTransitionName: isTransitioning ? `movie-poster-${id}` : "",
}}
/>
</div>
);
}
Dynamic Transition Names
import { useViewTransitionState } from "react-router";
function DynamicTransition({ item, targetPath }: { item: Item; targetPath: string }) {
const isTransitioning = useViewTransitionState(targetPath);
const transitionName = isTransitioning
? `${item.type}-${item.id}`
: "";
return (
<div
style={{
viewTransitionName: transitionName,
}}
>
{item.content}
</div>
);
}
Back Navigation Transitions
import { useViewTransitionState, useNavigate } from "react-router";
function DetailView({ id }: { id: string }) {
const navigate = useNavigate();
const isReturning = useViewTransitionState("/list");
return (
<div
style={{
viewTransitionName: isReturning ? `item-${id}` : "",
}}
>
<button onClick={() => navigate("/list", { viewTransition: true })}>
Back to List
</button>
{/* Detail content */}
</div>
);
}
CSS Integration
Defining View Transitions
/* Define the transition for the expanding image */
::view-transition-old(image-expand),
::view-transition-new(image-expand) {
animation-duration: 0.3s;
animation-timing-function: ease-in-out;
}
/* Customize the transition direction */
::view-transition-old(image-expand) {
animation-name: shrink;
}
::view-transition-new(image-expand) {
animation-name: expand;
}
@keyframes expand {
from {
transform: scale(0.8);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
@keyframes shrink {
from {
transform: scale(1);
opacity: 1;
}
to {
transform: scale(0.8);
opacity: 0;
}
}
Complex Transitions
import { useViewTransitionState } from "react-router";
function ComplexCard({ id }: { id: string }) {
const isTransitioning = useViewTransitionState(`/items/${id}`);
return (
<div className={isTransitioning ? "transitioning" : ""}>
<div
className="card-image"
style={{
viewTransitionName: isTransitioning ? `card-image-${id}` : "",
}}
/>
<div
className="card-title"
style={{
viewTransitionName: isTransitioning ? `card-title-${id}` : "",
}}
/>
<div
className="card-content"
style={{
viewTransitionName: isTransitioning ? `card-content-${id}` : "",
}}
/>
</div>
);
}
Requirements
-
View transitions must be enabled via the
viewTransition prop on navigation:
<Link to="/path" viewTransition>Link</Link>
<Form viewTransition>...</Form>
navigate("/path", { viewTransition: true });
submit(data, { viewTransition: true });
-
Browser support - View Transitions API is not supported in all browsers
-
Must be used within RouterProvider from
react-router-dom
Browser Support
View Transitions are a modern web API with limited browser support. Check Can I Use for current browser compatibility.
For unsupported browsers, the transition simply won’t occur, and navigation will work normally.
Notes
- Available in Framework and Data modes only
- Must be used within
react-router-dom’s RouterProvider (not the one from react-router)
- Returns
true for both forward and backward transitions to the specified location
- The
viewTransitionName CSS property must be set for transitions to work
- Each
viewTransitionName should be unique within the document
- Transitions work bidirectionally (going to and coming from the location)