Sharpen your frontend skills with a fresh HTML, CSS, or JavaScript question every day.
The View Transition API in 3 Examples
How to animate between pages using the View Transition API.
Smooth animations help make a UI feel polished and intuitive. Instead of abrupt changes between pages, adding graceful animations can really go a long way.
Adding these animations should be simple, but it hasn’t been easy on the web. Typically you've always had to use some amount of JavaScript and the end result was pretty complicated.
Fortunately, we now have the View Transition API. With View Transitions, you can create seamless animations between pages in your application or website.
In this article, you’ll learn the basics of the View Transition API with three CSS focused examples. In fact, this blog uses a variation of one of these examples!
Please note that this article focuses on cross document ("multi-page") transitions between basic HTML files, but view transitions can be done in single page applications as well.
As of the time of this writing, browser support for View Transitions is limited. Oddly enough, the demo in this article seems to work best in Safari 18.2.
Getting Started
The View Transition API introduces some new CSS pseudo elements and at-rules that can be used to manage the animation between pages.
The first step is to enable view transitions using the @view-transition
at-rule.
@view-transition {
navigation: auto;
}
This will opt the page into using a view transition, and because the browser has defaults for this transition, you’ll get a subtle fade effect without writing any other code!
Example 1 - Basic Fade
Let’s take what we did above a step further and control the fade speed.
We can do this just by adding some animation keyframes.
@keyframes old-out {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@keyframes new-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
Next we’ll use these keyframes along with the view-transition-old
and view-transition-new
pseudo elements.
These elements represent the old and new states of the view transition - the “old” being a static snapshot of the page before the transition, and the “new” being a live representation of the page after the transition.
&::view-transition-old(root) {
animation-name: old-out;
animation-duration: 1.5s;
}
&::view-transition-new(root) {
animation-name: new-in;
animation-duration: 1.5s;
}
We’re using “root” in the element selector here to target the root view transition just to keep things simple and because we only have one transition.
In other cases, you would use a view transition name instead of "root", like ::view-transition-old(card)
.
Optionally, instead of defining the
animation-duration
in multiple spots, you can use the::view-transition-group
element, which will apply to both the old and new.
Example 2 - Scroll Up
For the second example, we’ll add an animation that scrolls upwards between pages.
The code for this is very similar to the previous example.
@keyframes old-out {
0% {
transform: translateY(0);
opacity: 1;
}
100% {
transform: translateY(-100%);
opacity: 0;
}
}
@keyframes new-in {
0% {
transform: translateY(100%);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
The only difference in these keyframes is the addition of the transform
property.
Finally add the old and new view transition pseudo elements.
&::view-transition-old(root) {
animation-name: old-out;
animation-duration: 1.6s;
}
&::view-transition-new(root) {
animation-name: new-in;
animation-duration: 1.6s;
animation-delay: 1s;
}
Example 3 - Scale and Scroll
For the last example, we’ll add an animation that scales the visible page content slightly before moving horizontally to the next page and scaling back up.
Fortunately, we can continue using the same approach as the previous examples - defining keyframes and mapping them to the view transition elements.
@keyframes old-out {
0% {
scale: 1;
transform: translateX(0%);
}
50% {
scale: 0.8;
transform: translateX(0%);
}
100% {
scale: 0.8;
transform: translateX(-200%);
}
}
@keyframes new-in {
0% {
scale: 0.8;
transform: translateX(200%);
}
50% {
scale: 0.8;
transform: translateX(0%);
}
100% {
scale: 1;
transform: translateX(0%);
}
}
Next we’ll use a new psuedo element to apply a background to the animation while it’s occurring - view-transition-image-pair
. The view-transition-image-pair
element is basically just a container around the new and old elements, which works nicely for this particular effect.
::view-transition-image-pair(root) {
background: linear-gradient(
90deg,
rgba(34, 193, 195, 1) 0%,
rgba(253, 187, 45, 1) 100%
);
}
Finally we have the old and new pseudo elements just like before.
::view-transition-group(root) {
animation-duration: 2s;
}
&::view-transition-old(root) {
animation-name: old-out;
}
&::view-transition-new(root) {
animation-name: new-in;
animation-delay: 1s;
}
As you can see, even for more advanced animations, the code required is not very involved and we used no JavaScript at all!
Browser Support
As mentioned above, browser support is currently limited for View Transitions, so be sure to check what you need to support before using this in production.
Depending on the animation, if a browser doesn't support View Transitions, the page navigation will occur normally.
If you need to provide a fallback, you can use the following at-rule.
@supports not (view-transition-name: none) {
}