Skip to main content

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

to
To
required
The location to check for an active View Transition. Can be a string path or a partial Path object with pathname, search, and hash.
options
object
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

isTransitioning
boolean
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

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

  1. 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 });
    
  2. Browser support - View Transitions API is not supported in all browsers
  3. 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)

Build docs developers (and LLMs) love