View transitions for single page applications

A common pattern for web pages is to use JavaScript to dynamically replace the content on a page, without loading a new, full HTML document. This is called a Single Page Application, or SPA. View transitions give you a way to show continuity or context between the pages in your SPA.

Full page transitions

When your user navigates to a new view in your SPA, your framework replaces the DOM with new content. This makes the content just appear, but what if you want to provide a transition between the current content and the new content?

Transitions often show both the old and new views simultaneously, for example fading out the old view while fading in the new view. Because existing content is replaced, this was a challenge before view transitions.

To use view transitions, you will need to wrap the logic for changing the DOM in a callback. For these examples, we have a basic router implementation provided by a web component called MyRouter. How you enable view transitions will depend on the router and framework that you are using.

document.startViewTransition(() => updateTheDOMSomehow());

This enables the default transition, which fades out the old view while fading in the new view.

What's happening here? When you call document.startViewTransition(), the browser takes a snapshot of the old view. It then calls the callback function that you pass, which updates the DOM to the new view (but doesn't yet show it). When the callback function is complete, the browser starts the transition to the new content.

// Fallback for browsers that don't support this API:
if (!document.startViewTransition) {
  updateTheDOMSomehow();
  return;
} else {
  // With a View Transition:
  document.startViewTransition(() => updateTheDOMSomehow());
}

Customizing the transition

As you saw in the prior example, the default View Transition fades out the old view while fading in the new view. You can customize the transition to better match your site's style by styling pseudo elements generated by view transitions.

You can specify the leaving transition with ::view-transition-old() and the entering transition with ::view-transition-new(). You can also specify values for both with ::view-transition-group().

In this example, the old view will transition out using the slide-out-to-left transition and the new view will transition in using the slide-in-from-right transition. They both will have a duration of 200 milliseconds.

::view-transition-group(root){
  animation-duration: 200ms;
}

::view-transition-old(root) {
  animation-name: slide-out-to-left;
}

::view-transition-new(root) {
  animation-name: slide-in-from-right;
}

Different transitions based on context

You may want to have different transitions based on what the user is doing. For example, if clicking on a link from your home page slides the new view in from the right, you'd expect clicking a link to go back to your home page would slide the home view in from the left.

You can specify different animations using the :active-view-transition-type() pseudo class.

html:active-view-transition-type(forwards) {
  &::view-transition-old(root) {
    animation-name: slide-out-to-left;
  }

  &::view-transition-new(root) {
    animation-name: slide-in-from-right;
  }
}

You can then pick which view transition type to use when calling document.startViewTransition().

const direction = next === 'home' ? 'backwards' : 'forwards';

document.startViewTransition({
  update: updateTheDOMSomehow,
  types: [direction],
});

Transitioning specific elements

So far, you have only been applying a transition to the root element to transition the entire view. But you can also use view transitions to animate specific parts of your pages.

For example, you may have content in the old view that matches content in the new view. This could be the title of the content or an image. It could even be a thumbnail image on the old view, and a video in the new view.

First, you need to specify which elements to transition, using the view-transition-name property. For view transitions to work, for eachview-transition-name, there needs to be exactly one element before you call document.startViewTransition() and exactly one element after the callback in document.startViewTransition() completes.

In this example, there is a music player that shows album art, the title, and the artist. An alternate view shows the same content rearranged, with the addition of song lyrics.

In the preceding example, there is exactly one of each of the transitioned elements in the old and new view, and they even share the same selectors. The transitioned elements appear to move between their sizes and positions. The non-transitioned parts of the view fade in and out.

Take a look at a more involved example. For example, the home page on a blog may show a headline and image for each post, and these are also present on the full page view of a blog post. When navigating from the home page to a specific post, you may want to make it appear as if the title and image transition to their new location to provide context.

To do this for the title, you need to have a view-transition-name on the title element that is unique in the old view, shared with the title element in the new view, and unique on the new view. This is a challenge, because the home page has multiple headlines and images, and you don't know which one the user will click.

You have two options on how to solve this. You can choose to add a unique view-transition-name for each post on the home page, and then match that name on each full page post. You can generate these using a post's ID. Your other option is to use a generic view-transition-name, but only apply it after the user clicks on a post, but before calling document.startViewTransition().

Designing transitions

View transitions are a set of tools that you can use to guide your users and provide additional brand or context hints. You will likely use multiple techniques to find the transitions that work for your site.

Depending on the effect you are looking for, you may also need to tweak the elements or animations. In the previous example, several styles were adjusted to get the smooth transitions.

The headline has the rule width: fit-content, which is a useful style when you transition text that doesn't wrap (or has the same wrapping in both the old and new view. Otherwise, the transition may be between elements with different widths, which will make the transition less smooth.

The image is also a different aspect ratio in the old view and new view. The example modifies the animation and the object-fit property so that the transition appears smooth.

Respecting prefers-reduced-motion

A common reason users request reduced motion is that full-screen animations, like those that can be achieved with view transitions, can cause discomfort for people with vestibular motion disorders. You can disable animations using the prefers-reduced-motion media query. You could also choose to provide alternate animations that are more subtle, yet still convey how elements are connected.

@media (prefers-reduced-motion) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

Check your understanding

What is the name of the pseudo element representing the view before document.startViewTransition() is called?

::view-transition-previous
Incorrect.
::view-transition-prior
Incorrect.
::view-transition-old
Correct!
::view-transition-initial
Incorrect.

What is the default animation for a View transition?

Fade out old as new fades in
Correct!
Slide from left to right
Incorrect.
Fade old to white, fade to new
Incorrect.
Star wipe
Incorrect.

What is a page's default view-transition-name?

document
Incorrect.
shadow-root
Incorrect.
root
Correct!
body
Incorrect.