Skip to content

Last updated: First published:

View Transition API Overview

The View Transition API provides a smooth visual transition from the current state of the browser’s document to a next state.

The API introduces a CSS property called view-transition-name that can be used to mark DOM elements to be animated. It also provides a single function called startViewTransition(), which can be found in the window.document object. You call this function when you want to start the visual transition and you provide an update callback that can convert the current document to its future version. In cross-document navigation, the browser calls it automatically with an update callback that swaps in the next page.

This is how window.document.startViewTransition() is called. The result contains some promises and a function to cancel the visual transition. Level 2 implementations also support the provision of navigation types. For details see the API description.

call-startViewTransition.js
1
const viewTransition = document.startViewTransition(async () => {
2
... update window.document here ...
3
});

The visual effects that are shown during the transition are normal CSS animation (or JavaScript animations). These animations do not directly show and transform the content of the document, but old images and new images that are taken before or after the update callback is called.

The browser provides a set of default animations, but you are free to override these with any animation you like. The startViewTransition() function makes it very easy to add animations as visual transitions to the state transition triggered by the update callback.

Types of Animation

There are three types of animation that play in parallel during a view transition:

  • Exit animations of old images.
  • Entry animations of new images.
  • Morph animations that transform old images into new images.

The typical exit and entry animations change opacity from 1 to 0 and vice versa. The morph animation moves the old and new image from the old’s position and size to the new’s position and size1.

Default Fade Animation

Without further configuration, the default visual effect is a fade from the document as it looked before startViewTransition() was called to the document as it looks after the update callback was applied to it.

This is the document before the view transition
And this is the document after the view transition

This animation has the view transition name root. That name is automatically assigned by the browser to the <html> document element. During the animation phase, the browser inserts a tree of pseudo elements rooted at the <html> element. If the only defined view transition name is root the tree looks like this:

DOM view in browser dev-tools
1
<html>
2
⏷::view-transition
3
⏷::view-transition-group(root)
4
⏷::view-transition-image-pair(root)
5
::view-transition-old(root)
6
::view-transition-new(root)

The styles for the fade animation are automatically defined by the browser.2

1
:view-transition-old(root) {
2
animation-name: -ua-view-transition-fade-out;
3
}
4
:view-transition-new(root) {
5
animation-name: -ua-view-transition-fade-in;
6
}
7
@keyframes -ua-view-transition-fade-out {
8
to { opacity: 0; }
9
}
10
@keyframes -ua-view-transition-fade-in {
11
from { opacity: 0; }
12
}

Customize the Root Animation

You can replace the default animation with something else, e.g. a zoom effect:

For this to work, you define two CSS animations: One that lets the previous content disappear and one that lets the new content appear. Think of both as screenshots. These images are represented by the ::view-transition-old(root) and ::view-transition-new(root) pseudo elements marked above. Both are children of the ::view-transition-image-pair(root).

For a zoom effect, you override the default animations for the two images of the image pair with a scaling transition.

zoom.css
1
::view-transition-old {
2
animation: 1s normal both zoom;
3
}
4
::view-transition-new {
5
animation: 1s reverse both zoom;
6
}
7
@keyframes zoom {
8
from {
9
transform: scale(1);
10
}
11
to {
12
transform: scale(0);
13
}
14
}

The result looks like this:

This is the document before the view transition
And his is the document after the view transition

Additional Image-Pairs

You can define additional image pairs by giving DOM elements a view transition name. The declaration of a view transition with the name myName defines a new image pair called ::view-transition-image-pair(myName).

You can set the view-transition-name CSS property …

  • … by using the style attribute on an element
    <div style="view-transition-name: myName">...,
  • … via a CSS rule selector { view-transition-name: myName; }
  • … or by setting the style attribute using javascript: el.style.viewTransitionName = "myName"

View transition names can contain unicode characters but some characters need to be encoded. Backslash-encoding of ASCII characters besides A-Za-z0-9_- does not work well for all browsers. I.e. encoding ? as \? might not work reliably, but \3f will most likely.

The special name none is used to not set a view transition name or to remove it.

View transition names must be unique per document state.

Typically, the same name is used once in the original document and once in the updated document. It is not necessary that the elements with the same name are also the same or even similar. The same name may mark a heading in the original document and an image in the updated document.

  • If you have added the name of the view transition myName to the original document state, this adds a ::view-transition-old(myName) image to the image pair.

  • If the updated document state has a view transition named myName, a ::view-transition-new(myName) image is added to the image pair.

Despite the name, the image pair can therefore have one or two images.

If a image pair is missing the new image, the animation on the old image is also called an exit animation. If a image pair is missing the old image, the animation on the new image is also called an entry animation.

Like ::view-transition-old(root) or ::view-transition-new(root), the children of the new image pair also have the standard animation, which fades out the old image and fades in the new image. If both images exist, the combined effect of this animations is a cross-fade. These animations can be redefined in the same way as the images for root. You can use this to have different effects, timing and velocity for parts of the view.

Morph / Group Animations

For each view transition name myName the ::view-transition-image-pair(myName) pseudo element is the single child of a ::view-transition-group(myName)

For each image pair with two images, the browser automatically defines an additional morph animation for ::view-transition-group(myName). This animation transitions both images from the position, size and transformation1 of the old image to the position, size and transformation of the new image. As the fade-in and fade-out animations are also played in parallel, the combined effect is that of morphing parts on your screen. And you can control this animation with CSS just as you do for the fade-out and fade-in animations.

This is the document before the view transition
And this is the document after the view transition
view-transition-name: x

Please note that the original elements of the new images are hidden during rendering. This leaves empty spaces in the rendered view, which are filled by the new images. In the animation above, for example, the red block on the right-hand side in the after state is not displayed while the animation is running.

See the section on pseudo-scrolling morph animations if you are interested in the details.

Summary

The view transition API offers a single function that use a callback to update the state of the document, and a view-transition-name CSS property to mark elements of the before and/or after state of the document for animations.

Each view transition name x adds a ::view-transition-group(x) and a ::view-transition-image-pair(x). It will also add ::view-transition-old(x), if the name is declared in the original document, and/or ::view-transition-new(x), if the name is declared in the updated document.

  • ::view-transition-old(x) has a fade-out animation by default.
  • ::view-transition-new(x) has a fade-in animation by default.
  • ::view-transition-group(x) has a morph animation by default.
  • ::view-transition-image-pair(x) does not define an animation. It is merely used as a container to add CSS, e.g. the corresponding “blend mode”.
  • all pseudo elements are part of a ::view-transition tree rooted at the <html> document element.

All animations can be controlled by CSS and redefined to your liking.

Footnotes

  1. plus some other CSS properties, see https://drafts.csswg.org/css-view-transitions/#style-transition-pseudo-elements-algorithm. Here, “position” & “size” are technically also transformations, i.e. translation and scale to be precise. 2

  2. For full details, especially the conditional use of -ua-mix-blend-mode-plus-lighter, see the spec