Skip to content

Commit f613bfa

Browse files
committed
Document view transitions
1 parent 86b7724 commit f613bfa

2 files changed

Lines changed: 215 additions & 0 deletions

File tree

‎.vitepress/config.mjs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const sharedSidebar = [
4242
items: [
4343
{ text: 'Enable Turbo Router', link: '/turbo/setup' },
4444
{ text: 'Working with JavaScript', link: '/turbo/javascript' },
45+
{ text: 'View Transitions', link: '/turbo/view-transitions' },
4546
]
4647
}
4748

‎turbo/view-transitions.md‎

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
# View Transitions
2+
3+
The [View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API) is a native browser feature that animates DOM changes with smooth visual transitions. When combined with the turbo router, page navigations get an automatic cross-fade animation, making your multi-page site feel as polished as a single-page application.
4+
5+
Instead of the usual instant swap between pages, the browser captures the old page state, applies the DOM update, captures the new state, and animates between the two. All of this happens natively, with no JavaScript animation libraries needed.
6+
7+
## Enabling View Transitions
8+
9+
To enable view transitions, include the following meta tag in the `<head>` section of your page or layout, alongside the turbo router meta tag.
10+
11+
```html
12+
<head>
13+
<meta name="turbo-visit-control" content="enable" />
14+
<meta name="turbo-view-transition" content="same-origin" />
15+
</head>
16+
```
17+
18+
That's it. Every turbo-routed page navigation will now use a smooth cross-fade transition. Browsers that don't support the View Transitions API will fall back to the standard instant page swap with no errors or side effects.
19+
20+
::: tip
21+
The `same-origin` value follows the web platform convention for view transitions. Only same-origin navigations will be animated, which is the standard and expected behavior.
22+
:::
23+
24+
## Customizing the Transition
25+
26+
The default transition is a cross-fade on the entire page. You can customize the animation duration, easing, or style using CSS pseudo-elements provided by the View Transitions API.
27+
28+
### Adjusting Duration and Easing
29+
30+
```css
31+
::view-transition-old(root),
32+
::view-transition-new(root) {
33+
animation-duration: 0.3s;
34+
animation-timing-function: ease-in-out;
35+
}
36+
```
37+
38+
### Slide Transition
39+
40+
Replace the default cross-fade with a horizontal slide.
41+
42+
```css
43+
@keyframes slide-out {
44+
to { transform: translateX(-100%); }
45+
}
46+
47+
@keyframes slide-in {
48+
from { transform: translateX(100%); }
49+
}
50+
51+
::view-transition-old(root) {
52+
animation: slide-out 0.3s ease-in-out;
53+
}
54+
55+
::view-transition-new(root) {
56+
animation: slide-in 0.3s ease-in-out;
57+
}
58+
```
59+
60+
### Fade with Scale
61+
62+
A subtle zoom effect that works well for content-heavy pages.
63+
64+
```css
65+
::view-transition-old(root) {
66+
animation: 0.2s ease-in both fade-out, 0.3s ease-in both scale-down;
67+
}
68+
69+
::view-transition-new(root) {
70+
animation: 0.3s ease-out 0.1s both fade-in, 0.3s ease-out 0.1s both scale-up;
71+
}
72+
73+
@keyframes fade-out { to { opacity: 0; } }
74+
@keyframes fade-in { from { opacity: 0; } }
75+
@keyframes scale-down { to { transform: scale(0.95); } }
76+
@keyframes scale-up { from { transform: scale(0.95); } }
77+
```
78+
79+
## Directional Animations
80+
81+
During navigation, the turbo router sets a `data-turbo-visit-direction` attribute on the `<html>` element with one of three values:
82+
83+
Direction | When
84+
--------- | ----
85+
**forward** | Clicking a link (advance action)
86+
**back** | Browser back/forward (restore action)
87+
**none** | Replace or same-page actions
88+
89+
This lets you create directional slide animations that feel natural, sliding content in from the right when navigating forward and from the left when going back.
90+
91+
```css
92+
@keyframes slide-from-right {
93+
from { transform: translateX(100%); }
94+
}
95+
96+
@keyframes slide-to-left {
97+
to { transform: translateX(-100%); }
98+
}
99+
100+
@keyframes slide-from-left {
101+
from { transform: translateX(-100%); }
102+
}
103+
104+
@keyframes slide-to-right {
105+
to { transform: translateX(100%); }
106+
}
107+
108+
/* Forward navigation */
109+
html[data-turbo-visit-direction="forward"]::view-transition-old(root) {
110+
animation: slide-to-left 0.3s ease-in-out;
111+
}
112+
113+
html[data-turbo-visit-direction="forward"]::view-transition-new(root) {
114+
animation: slide-from-right 0.3s ease-in-out;
115+
}
116+
117+
/* Back navigation */
118+
html[data-turbo-visit-direction="back"]::view-transition-old(root) {
119+
animation: slide-to-right 0.3s ease-in-out;
120+
}
121+
122+
html[data-turbo-visit-direction="back"]::view-transition-new(root) {
123+
animation: slide-from-left 0.3s ease-in-out;
124+
}
125+
```
126+
127+
The attribute is removed after the visit completes, so it won't affect other CSS rules.
128+
129+
## Animating Specific Elements
130+
131+
The real power of view transitions comes from animating individual elements independently. By assigning a `view-transition-name` to an element, the browser will track it across page navigations and animate it separately from the rest of the page.
132+
133+
### Hero Image Example
134+
135+
On a product listing page:
136+
137+
```html
138+
<img src="/products/widget.jpg" style="view-transition-name: hero-image;" />
139+
```
140+
141+
On the product detail page, the same image with the same transition name:
142+
143+
```html
144+
<img src="/products/widget.jpg" style="view-transition-name: hero-image;" />
145+
```
146+
147+
The browser will smoothly morph the image from its position on the listing page to its position on the detail page, while cross-fading the rest of the content. This creates a natural sense of continuity between pages.
148+
149+
### Page Header Example
150+
151+
Keep the header stable while the content transitions beneath it.
152+
153+
```css
154+
header {
155+
view-transition-name: main-header;
156+
}
157+
158+
::view-transition-old(main-header),
159+
::view-transition-new(main-header) {
160+
animation: none; /* No animation, header stays in place */
161+
}
162+
```
163+
164+
This gives the feel of only the main content area changing while the navigation remains fixed.
165+
166+
### Dynamic Transition Names
167+
168+
For lists of items where each card should animate individually, assign unique transition names dynamically.
169+
170+
```html
171+
<div class="product-card" style="view-transition-name: product-42;">
172+
<h3>Widget</h3>
173+
<img src="/products/widget-thumb.jpg" />
174+
</div>
175+
```
176+
177+
::: warning
178+
Every `view-transition-name` must be unique on the page. If two elements share the same name, the transition will fail silently. When generating names dynamically, use a unique identifier like a database ID or slug.
179+
:::
180+
181+
## Combining with Page Rendering Events
182+
183+
View transitions work seamlessly with the existing [page rendering events](./javascript.md). You can still use `page:before-render` to pause rendering and run exit animations before the view transition begins.
184+
185+
```js
186+
addEventListener('page:before-render', async (event) => {
187+
event.preventDefault();
188+
189+
// Run exit animation before the transition
190+
await animateOut();
191+
192+
// Resume rendering, the view transition wraps the DOM swap
193+
event.detail.resume();
194+
});
195+
```
196+
197+
The view transition wrapping occurs inside the `resume()` callback, so deferred renders receive the transition animation too.
198+
199+
## Respecting User Preferences
200+
201+
Some users prefer reduced motion for accessibility reasons. You should respect this preference by disabling or simplifying animations using the `prefers-reduced-motion` media query.
202+
203+
```css
204+
@media (prefers-reduced-motion: reduce) {
205+
::view-transition-old(root),
206+
::view-transition-new(root) {
207+
animation-duration: 0s;
208+
}
209+
}
210+
```
211+
212+
## Browser Compatibility
213+
214+
The View Transitions API is supported in Chrome 111+, Edge 111+, Safari 18+, and Firefox 126+. When a browser does not support the API, the turbo router will fall back to the standard instant page swap, so it is safe to enable in production without affecting older browsers.

0 commit comments

Comments
 (0)