INDIE UNPKG

9.55 kBMarkdownView Raw
1---
2title: View Transitions
3---
4
5# View Transitions
6
7[MODES: framework, data, declarative]
8
9<br/>
10<br/>
11
12Use React's [`<ViewTransition>`][react-view-transition] component to animate client-side navigations. This requires React and React DOM 19.3 or later.
13
14<docs-warning>
15
16React Router's `viewTransition` props/options, `useViewTransitionState` hook, and `NavLink`'s `isTransitioning` render prop and `transitioning` class are deprecated in favor of React's `<ViewTransition>` component. The existing APIs continue to work, including with older React versions.
17
18</docs-warning>
19
20## Navigation View Transitions with React
21
22Wrap the outlet in a persistent `<ViewTransition>` boundary in a shared layout route. In Framework Mode, this can be your root route's default export; in Data and Declarative modes, use it as the component for a parent route with children:
23
24```tsx
25import { ViewTransition } from "react";
26import { Link, Outlet } from "react-router";
27
28export default function PageLayout() {
29 return (
30 <>
31 <nav>
32 <Link to="/">Home</Link>
33 <Link to="/about">About</Link>
34 </nav>
35 <ViewTransition>
36 <main>
37 <Outlet />
38 </main>
39 </ViewTransition>
40 </>
41 );
42}
43```
44
45Navigations that update the outlet cross-fade its contents. Keep the boundary mounted across navigations; you don't need to key it by location or remount the route tree. The navigation stays outside the animated region. Other updates inside the boundary can also animate when they run in a React Transition.
46
47React Router wraps router state updates in [`React.startTransition`][start-transition] by default, which lets React activate `<ViewTransition>` without additional configuration. React Transitions must remain enabled for navigation view transitions to work: do not set `useTransitions={false}` on your router.
48
49This applies to navigations from `<Link>`, `<NavLink>`, and `<Form>`, as well as programmatic navigation with `useNavigate` and `useSubmit`. No additional `startTransition` wrapper is needed.
50
51### Migrating from React Router's view transitions
52
53- Remove `viewTransition` from `<Link>`, `<NavLink>`, and `<Form>`, and remove `viewTransition: true` from navigation/submission options. React coordinates `document.startViewTransition()` itself; enabling both implementations can interrupt animations.
54- Replace `useViewTransitionState`, `isTransitioning`, and `.transitioning` styles with React's [View Transition classes][react-view-transition-classes]. Those router APIs only track the legacy transitions.
55- For shared elements such as an image moving from a list to a detail route, wrap each side in `<ViewTransition name="...">` with the same unique name. See React's [shared element example][react-shared-elements].
56- Keep React Transitions enabled. `useTransitions={false}` and synchronous updates such as `flushSync` can prevent React's animations.
57
58React's component is not a drop-in replacement for every legacy animation. In particular, React [skips view transitions for synchronous updates from `popstate`][react-router-transitions], so browser Back/Forward navigations may not animate. Browsers without View Transitions support still navigate normally.
59
60Respect reduced-motion preferences when adding animations:
61
62```css
63@media (prefers-reduced-motion: reduce) {
64 ::view-transition-group(*),
65 ::view-transition-old(*),
66 ::view-transition-new(*) {
67 animation: none !important;
68 }
69}
70```
71
72## Legacy View Transitions (Deprecated)
73
74The following examples document the deprecated APIs for existing applications. These APIs are available in Framework and Data modes.
75
76### Basic View Transition
77
78#### 1. Enable view transitions on navigation
79
80The simplest way to enable view transitions is by adding the `viewTransition` prop to your `Link`, `NavLink`, or `Form` components. This automatically wraps the navigation update in `document.startViewTransition()`.
81
82```tsx
83<Link to="/about" viewTransition>
84 About
85</Link>
86```
87
88Without any additional CSS, this provides a basic cross-fade animation between pages.
89
90#### 2. Enable view transitions with programmatic navigation
91
92When using programmatic navigation with the `useNavigate` hook, you can enable view transitions by passing the `viewTransition: true` option:
93
94```tsx
95import { useNavigate } from "react-router";
96
97function NavigationButton() {
98 const navigate = useNavigate();
99
100 return (
101 <button
102 onClick={() =>
103 navigate("/about", { viewTransition: true })
104 }
105 >
106 About
107 </button>
108 );
109}
110```
111
112This provides the same cross-fade animation as using the `viewTransition` prop on Link components.
113
114For more information on using the View Transitions API, please refer to the ["Smooth transitions with the View Transition API" guide][view-transitions-guide] from the Google Chrome team.
115
116### Image Gallery Example
117
118Let's build an image gallery that demonstrates how to trigger and use view transitions. We'll create a list of images that expand into a detail view with smooth animations.
119
120#### 1. Create the image gallery route
121
122```tsx filename=routes/image-gallery.tsx
123import { NavLink } from "react-router";
124
125export const images = [
126 "https://remix.run/blog-images/headers/the-future-is-now.jpg",
127 "https://remix.run/blog-images/headers/waterfall.jpg",
128 "https://remix.run/blog-images/headers/webpack.png",
129 // ... more images ...
130];
131
132export default function ImageGalleryRoute() {
133 return (
134 <div className="image-list">
135 <h1>Image List</h1>
136 <div>
137 {images.map((src, idx) => (
138 <NavLink
139 key={src}
140 to={`/image/${idx}`}
141 viewTransition // Enable view transitions for this link
142 >
143 <p>Image Number {idx}</p>
144 <img
145 className="max-w-full contain-layout"
146 src={src}
147 />
148 </NavLink>
149 ))}
150 </div>
151 </div>
152 );
153}
154```
155
156#### 2. Add transition styles
157
158Define view transition names and animations for elements that should transition smoothly between routes.
159
160```css filename=app.css
161/* Layout styles for the image grid */
162.image-list > div {
163 display: grid;
164 grid-template-columns: repeat(4, 1fr);
165 column-gap: 10px;
166}
167
168.image-list h1 {
169 font-size: 2rem;
170 font-weight: 600;
171}
172
173.image-list img {
174 max-width: 100%;
175 contain: layout;
176}
177
178.image-list p {
179 width: fit-content;
180}
181
182/* Assign transition names to elements during navigation */
183.image-list a.transitioning img {
184 view-transition-name: image-expand;
185}
186
187.image-list a.transitioning p {
188 view-transition-name: image-title;
189}
190```
191
192#### 3. Create the image detail route
193
194The detail view needs to use the same view transition names to create a seamless animation.
195
196```tsx filename=routes/image-details.tsx
197import { Link } from "react-router";
198import { images } from "./home";
199import type { Route } from "./+types/image-details";
200
201export default function ImageDetailsRoute({
202 params,
203}: Route.ComponentProps) {
204 return (
205 <div className="image-detail">
206 <Link to="/" viewTransition>
207 Back
208 </Link>
209 <h1>Image Number {params.id}</h1>
210 <img src={images[Number(params.id)]} />
211 </div>
212 );
213}
214```
215
216#### 4. Add matching transition styles for the detail view
217
218```css filename=app.css
219/* Match transition names from the list view */
220.image-detail h1 {
221 font-size: 2rem;
222 font-weight: 600;
223 width: fit-content;
224 view-transition-name: image-title;
225}
226
227.image-detail img {
228 max-width: 100%;
229 contain: layout;
230 view-transition-name: image-expand;
231}
232```
233
234### Advanced Usage
235
236You can control view transitions more precisely using either render props or the `useViewTransitionState` hook.
237
238#### 1. Using render props
239
240```tsx filename=routes/image-gallery.tsx
241<NavLink to={`/image/${idx}`} viewTransition>
242 {({ isTransitioning }) => (
243 <>
244 <p
245 style={{
246 viewTransitionName: isTransitioning
247 ? "image-title"
248 : "none",
249 }}
250 >
251 Image Number {idx}
252 </p>
253 <img
254 src={src}
255 style={{
256 viewTransitionName: isTransitioning
257 ? "image-expand"
258 : "none",
259 }}
260 />
261 </>
262 )}
263</NavLink>
264```
265
266#### 2. Using the `useViewTransitionState` hook
267
268```tsx filename=routes/image-gallery.tsx
269function NavImage(props: { src: string; idx: number }) {
270 const href = `/image/${props.idx}`;
271 // Hook provides transition state for specific route
272 const isTransitioning = useViewTransitionState(href);
273
274 return (
275 <Link to={href} viewTransition>
276 <p
277 style={{
278 viewTransitionName: isTransitioning
279 ? "image-title"
280 : "none",
281 }}
282 >
283 Image Number {props.idx}
284 </p>
285 <img
286 src={props.src}
287 style={{
288 viewTransitionName: isTransitioning
289 ? "image-expand"
290 : "none",
291 }}
292 />
293 </Link>
294 );
295}
296```
297
298[view-transitions-api]: https://developer.mozilla.org/en-US/docs/Web/API/ViewTransition
299[react-view-transition]: https://react.dev/reference/react/ViewTransition
300[start-transition]: https://react.dev/reference/react/startTransition
301[react-view-transition-classes]: https://react.dev/reference/react/ViewTransition#view-transition-class
302[react-shared-elements]: https://react.dev/reference/react/ViewTransition#animating-a-shared-element
303[react-router-transitions]: https://react.dev/reference/react/ViewTransition#building-view-transition-enabled-routers
304[view-transitions-guide]: https://developer.chrome.com/docs/web-platform/view-transitions