Cross-document view transitions for multi-page applications  |  View Transitions  |  Chrome for Developers (2024)

Cross-document view transitions for multi-page applications | View Transitions | Chrome for Developers (1)


When a view transition occurs between two different documents it is called a cross-document view transition. This is typically the case in multi-page applications (MPA). Cross-document view transitions are supported in Chrome from Chrome 126.

Browser Support

  • 126
  • 126
  • x
  • x


Cross-document view transitions rely on the very same building blocks and principles as same-document view transitions, which is very intentional:

  1. The browser takes snapshots of elements that have a unique view-transition-name on both the old and new page.
  2. The DOM gets updated while rendering is suppressed.
  3. And finally, the transitions are powered by CSS animations.

What's different when compared with same-document view transitions, is that with cross-document view transitions you don't need to call document.startViewTransition to start a view transition. Instead, the trigger for a cross-document view transition is a same-origin navigation from one page to another, an action that is typically performed by the user of your website clicking a link.

In other words, there is no API to call in order to start a view transition between two documents. However, there are two conditions that need to be fulfilled:

  • Both documents need to exist on the same origin.
  • Both pages need to opt-in to allow the view transition.

Both these conditions are explained later in this document.

Cross-document view transitions are limited to same-origin navigations

Cross-document view transitions are limited to same-origin navigations only. A navigation is considered to be same-origin if the origin of both participating pages is the same.

The origin of a page is a combination of the used scheme, hostname, and port, as detailed on

Cross-document view transitions for multi-page applications | View Transitions | Chrome for Developers (2)

For example, you can have a cross-document view transition when navigating from to, as those are same-origin.You can't have that transition when navigating from to, as those are cross-origin and same-site.

Cross-document view transitions are opt-in

To have a cross-document view transition between two documents, both participating pages need to opt-in to allowing this. This is done with the @view-transition at-rule in CSS.

In the @view-transition at-rule, set the navigation descriptor to auto to enable view transitions for cross-document, same-origin navigations.

@view-transition { navigation: auto;}

By setting navigation descriptor to auto you are opting in to allowing view transitions to happen for the following NavigationTypes:

  • traverse
  • push or replace, if the activation was not initiated by the user through browser UI mechanisms.

Navigations excluded from auto are, for example, navigating using the URL address bar or clicking a bookmark, as well as any form of user or script initiated reload.

If a navigation takes too long–more than four seconds in Chrome's case–then the view transition is skipped with a TimeoutError DOMException.

Cross-document view transitions demo

Check out the following demo that uses view transitions to create a Stack Navigator demo. There are no calls to document.startViewTransition() here, the view transitions are triggered by navigating from one page to another.

Customize cross-document view transitions

To customize cross-document view transitions, there are some web platform features that you can use.

  • The pageswap and pagereveal events
  • Navigation activation information
  • Render blocking

These features are not part of the View Transition API specification itself, but are designed to be used in conjunction with it.

The pageswap and pagereveal events

Browser Support

  • 124
  • 124
  • x
  • x


To allow you to customize cross-document view transitions, the HTML specification includes two new events that you can use: pageswap and pagereveal.

These two events get fired for every same-origin cross-document navigation regardless of whether a view transition is about to happen or not. If a view transition is about to happen between the two pages, you can access the ViewTransition object using the viewTransition property on these events.

  • The pageswap event fires before the last frame of a page is rendered. You can use this to do some last-minute changes on the outgoing page, right before the old snapshots get taken.
  • The pagereveal event fires on a page after it has been initialized or reactivated but before the first rendering opportunity. With it, you can customize the new page before the new snapshots get taken.

For example, you can use these events to quickly set or change some view-transition-name values or pass data from one document to another by writing and reading data from sessionStorage to customize the view transition before it actually runs.

let lastClickX, lastClickY;document.addEventListener('click', (event) => { if ( === 'a') return; lastClickX = event.clientX; lastClickY = event.clientY;});// Write position to storage on old pagewindow.addEventListener('pageswap', (event) => { if (event.viewTransition && lastClick) { sessionStorage.setItem('lastClickX', lastClickX); sessionStorage.setItem('lastClickY', lastClickY); }});// Read position from storage on new pagewindow.addEventListener('pagereveal', (event) => { if (event.viewTransition) { lastClickX = sessionStorage.getItem('lastClickX'); lastClickY = sessionStorage.getItem('lastClickY'); }});

If you want, you can decide to skip the transition in both events.

window.addEventListener("pagereveal", async (e) => { if (e.viewTransition) { if (goodReasonToSkipTheViewTransition()) { e.viewTransition.skipTransition(); } }}

The ViewTransition object in pageswap and pagereveal are two different objects. They also handle the various promises differently:

  • pageswap: Once the document is hidden, the old ViewTransition object is skipped. When that happens, viewTransition.ready rejects and viewTransition.finished resolves.
  • pagereveal: The updateCallBack promise is already resolved at this point. You can use the viewTransition.ready and viewTransition.finished promises.

Navigation activation information

Browser Support

  • 123
  • 123
  • x
  • x


In both pageswap and pagereveal events, you can also take action based on the URLs of the old and new pages.

For example, in the MPA Stack Navigator the type of animation to use depends the navigation path:

  • When navigating from the overview page to a detail page, the new content needs to slide in from the right to the left.
  • When navigating from the detail page to the overview page, the old content needs to slide out from the left to the right.

To do this you need information about the navigation that, in the case of pageswap, is about to happen or, in the case of pagereveal just happened.

For this, browsers can now expose NavigationActivation objects which hold info about the same-origin navigation. This object exposes the used navigation type, the current, and the final destination history entries as found in navigation.entries() from the Navigation API.

On an activated page, you can access this object through navigation.activation. In the pageswap event, you can access this through e.activation.

Check out this Profiles demo that uses NavigationActivation info in the pageswap and pagereveal events to set the view-transition-name values on the elements that need to participate in the view transition.

That way, you don't have to decorate each and every item in the list with a view-transition-name upfront. Instead, this happens just-in-time using JavaScript, only on elements that need it.

The code is as follows:

// OLD PAGE LOGICwindow.addEventListener('pageswap', async (e) => { if (e.viewTransition) { const targetUrl = new URL(e.activation.entry.url); // Navigating to a profile page if (isProfilePage(targetUrl)) { const profile = extractProfileNameFromUrl(targetUrl); // Set view-transition-name values on the clicked row document.querySelector(`#${profile} span`).style.viewTransitionName = 'name'; document.querySelector(`#${profile} img`).style.viewTransitionName = 'avatar'; // Remove view-transition-names after snapshots have been taken // (this to deal with BFCache) await e.viewTransition.finished; document.querySelector(`#${profile} span`).style.viewTransitionName = 'none'; document.querySelector(`#${profile} img`).style.viewTransitionName = 'none'; } }});// NEW PAGE LOGICwindow.addEventListener('pagereveal', async (e) => { if (e.viewTransition) { const fromURL = new URL(navigation.activation.from.url); const currentURL = new URL(navigation.activation.entry.url); // Navigating from a profile page back to the homepage if (isProfilePage(fromURL) && isHomePage(currentURL)) { const profile = extractProfileNameFromUrl(currentURL); // Set view-transition-name values on the elements in the list document.querySelector(`#${profile} span`).style.viewTransitionName = 'name'; document.querySelector(`#${profile} img`).style.viewTransitionName = 'avatar'; // Remove names after snapshots have been taken // so that we're ready for the next navigation await e.viewTransition.ready; document.querySelector(`#${profile} span`).style.viewTransitionName = 'none'; document.querySelector(`#${profile} img`).style.viewTransitionName = 'none'; } }});

The code also cleans up after itself by removing the view-transition-name values after the view transition ran. This way the page is ready for successive navigations and can also handle traversal of the history.

To aid with this, use this utility function that temporarily sets view-transition-names.

const setTemporaryViewTransitionNames = async (entries, vtPromise) => { for (const [$el, name] of entries) { $ = name; } await vtPromise; for (const [$el, name] of entries) { $ = ''; }}

The previous code can now be simplified as follows:

// OLD PAGE LOGICwindow.addEventListener('pageswap', async (e) => { if (e.viewTransition) { const targetUrl = new URL(e.activation.entry.url); // Navigating to a profile page if (isProfilePage(targetUrl)) { const profile = extractProfileNameFromUrl(targetUrl); // Set view-transition-name values on the clicked row // Clean up after the page got replaced setTemporaryViewTransitionNames([ [document.querySelector(`#${profile} span`), 'name'], [document.querySelector(`#${profile} img`), 'avatar'], ], e.viewTransition.finished); } }});// NEW PAGE LOGICwindow.addEventListener('pagereveal', async (e) => { if (e.viewTransition) { const fromURL = new URL(navigation.activation.from.url); const currentURL = new URL(navigation.activation.entry.url); // Navigating from a profile page back to the homepage if (isProfilePage(fromURL) && isHomePage(currentURL)) { const profile = extractProfileNameFromUrl(currentURL); // Set view-transition-name values on the elements in the list // Clean up after the snapshots have been taken setTemporaryViewTransitionNames([ [document.querySelector(`#${profile} span`), 'name'], [document.querySelector(`#${profile} img`), 'avatar'], ], e.viewTransition.ready); } }});

Wait for content to load with render blocking

Browser Support

  • 124
  • 124
  • x
  • x


In some cases, you may want to hold off the first render of a page until a certain element is present in the new DOM. This avoids flashing and ensure the state you're animating to is stable.

In the <head>, define one or more element IDs that need to be present before the page gets its first render, using the following meta tag.

<link rel="expect" blocking="render" href="#section1">

This meta tag means that the element should be present in the DOM, not that the content should be loaded. For example with images, the mere presence of the <img> tag with the specified id in the DOM tree is enough for the condition to evaluate to true. The image itself could still be loading.

Before you go all-in on render blocking be aware that incremental rendering is a fundamental aspect of the Web, so be cautious when opting to blocking rendering. The impact of blocking rendering needs to be evaluated on a case by case basis. By default, avoid using blocking=render unless you can actively measure and gauge the impact it has on your users, by measuring the impact to your Core Web Vitals.

View transition types in cross-document view transitions

Cross-document view transitions also support view transition types to customize the animations and which elements get captured.

For example, when going to the next or to the previous page in a pagination, you might want to use different animations depending on whether you are going to a higher page or a lower page from the sequence.

To set these types upfront, add the types in the @view-transition at-rule:

@view-transition { navigation: auto; types: slide, forwards;}

To set the types on the fly, use the pageswap and pagereveal events to manipulate the value of e.viewTransition.types.

window.addEventListener("pagereveal", async (e) => { if (e.viewTransition) { const transitionType = determineTransitionType(navigation.activation.from, navigation.activation.entry); e.viewTransition.types.add(transitionType); }});

The types are not automatically carried over from the ViewTransition object on the old page to the ViewTransition object of the new page. You need to determine the type(s) to use on at least the new page in order for the animations to run as expected.

To respond to these types, use the :active-view-transition-type() pseudo-class selector in the same way as with same-document view transitions

/* Determine what gets captured when the type is forwards or backwards */html:active-view-transition-type(forwards, backwards) { :root { view-transition-name: none; } article { view-transition-name: content; } .pagination { view-transition-name: pagination; }}/* Animation styles for forwards type only */html:active-view-transition-type(forwards) { &::view-transition-old(content) { animation-name: slide-out-to-left; } &::view-transition-new(content) { animation-name: slide-in-from-right; }}/* Animation styles for backwards type only */html:active-view-transition-type(backwards) { &::view-transition-old(content) { animation-name: slide-out-to-right; } &::view-transition-new(content) { animation-name: slide-in-from-left; }}/* Animation styles for reload type only */html:active-view-transition-type(reload) { &::view-transition-old(root) { animation-name: fade-out, scale-down; } &::view-transition-new(root) { animation-delay: 0.25s; animation-name: fade-in, scale-up; }}

Because types only apply to an active view transition, types automatically get cleaned up when a view transition finishes. Because of that, types work well with features like BFCache.


In the following pagination demo, the page contents slide forwards or backwards based on the page number that you are navigating to.

The transition type to use is determined in the pagereveal and pageswap events by looking at the to and from URLs.

const determineTransitionType = (fromNavigationEntry, toNavigationEntry) => { const currentURL = new URL(fromNavigationEntry.url); const destinationURL = new URL(toNavigationEntry.url); const currentPathname = currentURL.pathname; const destinationPathname = destinationURL.pathname; if (currentPathname === destinationPathname) { return "reload"; } else { const currentPageIndex = extractPageIndexFromPath(currentPathname); const destinationPageIndex = extractPageIndexFromPath(destinationPathname); if (currentPageIndex > destinationPageIndex) { return 'backwards'; } if (currentPageIndex < destinationPageIndex) { return 'forwards'; } return 'unknown'; }};


Developer feedback is always appreciated. To share, file an issue with the CSS Working Group on GitHub with suggestions and questions. Prefix your issue with [css-view-transitions].Should you run into a bug, then file a Chromium bug instead.

Cross-document view transitions for multi-page applications  |  View Transitions  |  Chrome for Developers (2024)


What is the default animation of view transitions? ›

By default, View Transitions API will perform a cross-fade animation between the old (fade-out) and new (fade-in) states. We're merely crossfading between the two screen states, and that includes all elements within it (i.e., other images, cards, grid, and so on).

What is the view transition API? ›

The View Transition API gives you the power to create seamless visual transitions between different views on your website. This creates a more visually engaging user experience for users as they navigate your site, regardless of whether it's built as a multi-page application (MPA) or a single-page application (SPA).

What does page transition mean? ›

In a general sense, a page transition just means moving from one page on a website to another in a fluid manner. Instead of the page just switching to another page, a page transition adds an engaging element to the website and gives your visitors something to look at while the pages change and load.

What is the view transition name in CSS? ›

The view-transition-name CSS property provides the selected element with a distinct identifying name (a <custom-ident> ) and causes it to participate in a separate view transition from the root view transition — or no view transition if the none value is specified.

What is transition animation scale in developer options? ›

Transition animation scale: The speed of moving between home screen panels and within apps. Animator duration scale: The speed of general UI animations, such as opening and closing apps, unlocking the phone, etc.

What is animation API? ›

The Web Animations API allows for synchronizing and timing changes to the presentation of a Web page, i.e. animation of DOM elements.

What is a pivot transition? ›

Pivoting is making a quick turn when change has happened by surprise or without you planning for it. Pivoting allows for a different view; adjusting your direction and continuing your momentum toward your goal or mission. The word” stop” or giving up has no place in the process of change, transition or pivot.

What is motion API? ›

The Motion API empowers users to streamline project management and productivity tasks. Within Pipedream's environment, you can leverage this API to automate actions based on project updates, task completions, and team collaborations.

What are examples of transitions? ›

and, again, and then, besides, equally important, finally, further, furthermore, nor, too, next, lastly, what's more, moreover, in addition, first (second, etc.)

What is the purpose of a transition document? ›

A transition plan or handover note is a document or set of documents created to facilitate the process of transferring knowledge, responsibilities, and tasks from one person or team to another. These documents are essential to ensuring continuity and minimizing disruption in workflows, projects, or services.

What are CSS transitions best used for? ›

CSS transitions allows you to change property values smoothly, over a given duration. In this chapter you will learn about the following properties: transition. transition-delay.

What is the alternative to transitions in CSS? ›

CSS Animations are a more powerful alternative to transitions. Rather than rely on a change from one beginning state to an end state, animations can be made up of as many in-between states as you like, and offer more control over how the states are animated.

How do you activate transitions in CSS? ›

Transition triggers

Your CSS must include a change of state and an event that triggers that state change for CSS transitions to activate. A typical example of such a trigger is the :hover pseudo-class. This pseudo-class matches when the user hovers over an element with their cursor.

What are the default transitions in OBS? ›

By default, OBS has two scene transitions created for you: Cut and Fade. A Cut transition instantly switches between scenes. A Fade transition causes the scene you are switching to fade in over duration of time that you can adjust by increasing or decreasing the value of milliseconds.

What is the default transition in Premiere? ›

Cross Dissolve is the default video transition used in Premiere Pro.

What is the default value of animation? ›

If the animation-duration property is not specified, no animation will occur, because the default value is 0s (0 seconds).

What is the default animation in IOS? ›

The default animation is spring(response:dampingFraction:blendDuration:) with: response equal to 0.

Top Articles
Latest Posts
Article information

Author: Msgr. Benton Quitzon

Last Updated:

Views: 5751

Rating: 4.2 / 5 (63 voted)

Reviews: 86% of readers found this page helpful

Author information

Name: Msgr. Benton Quitzon

Birthday: 2001-08-13

Address: 96487 Kris Cliff, Teresiafurt, WI 95201

Phone: +9418513585781

Job: Senior Designer

Hobby: Calligraphy, Rowing, Vacation, Geocaching, Web surfing, Electronics, Electronics

Introduction: My name is Msgr. Benton Quitzon, I am a comfortable, charming, thankful, happy, adventurous, handsome, precious person who loves writing and wants to share my knowledge and understanding with you.