UNPKG

5.78 kB Markdown View Raw
1---
2title: View Transitions
3---
4
5# View Transitions
6
7[MODES: framework, data]
8
9<br/>
10<br/>
11
12Enable smooth animations between page transitions in your React Router applications using the [View Transitions API][view-transitions-api]. This feature allows you to create seamless visual transitions during client-side navigation.
13
14## Basic View Transition
15
16### 1. Enable view transitions on navigation
17
18The 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()`.
19
20```tsx
21<Link to="/about" viewTransition>
22 About
23</Link>
24```
25
26Without any additional CSS, this provides a basic cross-fade animation between pages.
27
28### 2. Enable view transitions with programmatic navigation
29
30When using programmatic navigation with the `useNavigate` hook, you can enable view transitions by passing the `viewTransition: true` option:
31
32```tsx
33import { useNavigate } from "react-router";
34
35function NavigationButton() {
36 const navigate = useNavigate();
37
38 return (
39 <button
40 onClick={() =>
41 navigate("/about", { viewTransition: true })
42 }
43 >
44 About
45 </button>
46 );
47}
48```
49
50This provides the same cross-fade animation as using the `viewTransition` prop on Link components.
51
52For 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.
53
54## Image Gallery Example
55
56Let'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.
57
58### 1. Create the image gallery route
59
60```tsx filename=routes/image-gallery.tsx
61import { NavLink } from "react-router";
62
63export const images = [
64 "https://remix.run/blog-images/headers/the-future-is-now.jpg",
65 "https://remix.run/blog-images/headers/waterfall.jpg",
66 "https://remix.run/blog-images/headers/webpack.png",
67 // ... more images ...
68];
69
70export default function ImageGalleryRoute() {
71 return (
72 <div className="image-list">
73 <h1>Image List</h1>
74 <div>
75 {images.map((src, idx) => (
76 <NavLink
77 key={src}
78 to={`/image/${idx}`}
79 viewTransition // Enable view transitions for this link
80 >
81 <p>Image Number {idx}</p>
82 <img
83 className="max-w-full contain-layout"
84 src={src}
85 />
86 </NavLink>
87 ))}
88 </div>
89 </div>
90 );
91}
92```
93
94### 2. Add transition styles
95
96Define view transition names and animations for elements that should transition smoothly between routes.
97
98```css filename=app.css
99/* Layout styles for the image grid */
100.image-list > div {
101 display: grid;
102 grid-template-columns: repeat(4, 1fr);
103 column-gap: 10px;
104}
105
106.image-list h1 {
107 font-size: 2rem;
108 font-weight: 600;
109}
110
111.image-list img {
112 max-width: 100%;
113 contain: layout;
114}
115
116.image-list p {
117 width: fit-content;
118}
119
120/* Assign transition names to elements during navigation */
121.image-list a.transitioning img {
122 view-transition-name: image-expand;
123}
124
125.image-list a.transitioning p {
126 view-transition-name: image-title;
127}
128```
129
130### 3. Create the image detail route
131
132The detail view needs to use the same view transition names to create a seamless animation.
133
134```tsx filename=routes/image-details.tsx
135import { Link } from "react-router";
136import { images } from "./home";
137import type { Route } from "./+types/image-details";
138
139export default function ImageDetailsRoute({
140 params,
141}: Route.ComponentProps) {
142 return (
143 <div className="image-detail">
144 <Link to="/" viewTransition>
145 Back
146 </Link>
147 <h1>Image Number {params.id}</h1>
148 <img src={images[Number(params.id)]} />
149 </div>
150 );
151}
152```
153
154### 4. Add matching transition styles for the detail view
155
156```css filename=app.css
157/* Match transition names from the list view */
158.image-detail h1 {
159 font-size: 2rem;
160 font-weight: 600;
161 width: fit-content;
162 view-transition-name: image-title;
163}
164
165.image-detail img {
166 max-width: 100%;
167 contain: layout;
168 view-transition-name: image-expand;
169}
170```
171
172## Advanced Usage
173
174You can control view transitions more precisely using either render props or the `useViewTransitionState` hook.
175
176### 1. Using render props
177
178```tsx filename=routes/image-gallery.tsx
179<NavLink to={`/image/${idx}`} viewTransition>
180 {({ isTransitioning }) => (
181 <>
182 <p
183 style={{
184 viewTransitionName: isTransitioning
185 ? "image-title"
186 : "none",
187 }}
188 >
189 Image Number {idx}
190 </p>
191 <img
192 src={src}
193 style={{
194 viewTransitionName: isTransitioning
195 ? "image-expand"
196 : "none",
197 }}
198 />
199 </>
200 )}
201</NavLink>
202```
203
204### 2. Using the `useViewTransitionState` hook
205
206```tsx filename=routes/image-gallery.tsx
207function NavImage(props: { src: string; idx: number }) {
208 const href = `/image/${props.idx}`;
209 // Hook provides transition state for specific route
210 const isTransitioning = useViewTransitionState(href);
211
212 return (
213 <Link to={href} viewTransition>
214 <p
215 style={{
216 viewTransitionName: isTransitioning
217 ? "image-title"
218 : "none",
219 }}
220 >
221 Image Number {props.idx}
222 </p>
223 <img
224 src={props.src}
225 style={{
226 viewTransitionName: isTransitioning
227 ? "image-expand"
228 : "none",
229 }}
230 />
231 </Link>
232 );
233}
234```
235
236[view-transitions-api]: https://developer.mozilla.org/en-US/docs/Web/API/ViewTransition
237[view-transitions-guide]: https://developer.chrome.com/docs/web-platform/view-transitions