Skip to content

Making Seamless Page Transitions with the View Transitions API

Making Seamless Page Transitions with the View Transitions API

Make Seamless Page Transitions using the View Transitions API

Traditionally web applications have always had a less polished experience, both functionally and visually speaking, compared to native applications on mobile and other platforms. This has been improving over the years with the introduction of new web APIs and standards.

One such new API that bridges that gap is the View Transitions API.

What is the View Transitions API?

The View Transitions API is a new browser feature that allows developers to more easily create animated transitions between pages and views, similar to how mobile apps have transitions when navigating between pages.

Adding view transitions to your application can reduce the cognitive load on your users and make the experience feel less inconsistent. One great thing about view transitions is that they can be used by both SPAs (single-page applications) and MPAs (multi-page applications) alike!

Using the View Transitions API for SPAs

Let’s see how we can use view transitions ourselves. We will use examples adapted from demos created by the wonderful Jake Archibald and Bramus. There are some alterations to a few of the examples to make them work with the newest version of the View Transitions API, but otherwise, they’re mostly the same.

The process of getting view transitions working for SPAs is as simple as calling document.startViewTransition right before starting navigation and replacing your page’s content immediately after that. You need to make this call in your router or in an event listener that triggers your page navigation.

The following is an example of how you can hook into page navigation in a vanilla JavaScript application. This example uses the Navigation API if it’s available. As of the time of writing this article only Chromium-based browsers support the Navigation API, but there is a fallback to using a click listener on link elements so that this works with Firefox and Safari.

export async function onLinkNavigate(callback) {
  // Fallback to intercepting click events on <a> tags if the
  // Navigation API is not supported by the browser.
  //
  // For modern JavaScript frameworks with client-side routing
  // you would want to hook into the router instead as <a> tags
  // may not be the only way pages are navigated to.
  if (!self.navigation) {
    document.addEventListener('click', (event) => {
      const link = event.target.closest('a');
      if (!link) return;

      event.preventDefault();

      const toUrl = new URL(link.href);

      callback({
        toPath: toUrl.pathname,
        fromPath: location.pathname,
        isBack: false,
      });

      history.pushState({}, null, toUrl.pathname);
    });
    return;
  }

  // Use the Navigation API if it is available.
  navigation.addEventListener('navigate', (event) => {
    const toUrl = new URL(event.destination.url);

    // Do not intercept the event if we're navigating to
    // URL on another domain origin.
    if (location.origin !== toUrl.origin) return;

    const fromPath = location.pathname;
    const isBack = isBackNavigation(event);

    event.intercept({
      async handler() {
        if (event.info === 'ignore') return;

        await callback({
          toPath: toUrl.pathname,
          fromPath,
          isBack,
        });
      },
    });
  });
}

If you’re using a framework, then there’s probably a better way to do this specific to that framework than by using this contrived example. For example, if you are implementing usage of this API with a Next application, you may opt for something like next-view-transitions.

For this article, we’re going to focus on the way this is done in vanilla JavaScript so that the fundamentals of using the API are understood. Knowing how it’s implemented in vanilla JavaScript should give you enough understanding to implement this in the framework of your choice if there isn’t already a library that does it for you.

Now we have a way to know when a navigation is being requested. Using the above utility function we can start the animation and replace our content as follows:

onLinkNavigate(async ({ toPath }) => {
  const content = await getMyPageContentSomehow(toPath);

  startViewTransition(() => {
    document.body.innerHTML = content;
  });
});

function startViewTransition(callback) {
  if (!document.startViewTransition) {
    callback();
    return;
  }

  document.startViewTransition(callback);
}

getMyPageContentSomehow can be any function that returns HTML. How that is implemented will depend on your application and framework of choice.

If you want to run these examples on your machine, the demo repository has instructions in its README. In essence, it’s as simple as hosting an HTTP server at the root of the project and visiting localhost. No fancy build system is required!

By default, a fade is done that only lasts for a moment. The animation that happens can be configured using CSS with the ::view-transition-group pseudo-element on the element you want to animate, which in our case is the entire page, and we pass in root.

::view-transition-group(root) {
  animation-duration: 5s;
}

Given that the animation properties are used, you have much control over what kind of animation to do. You can change the duration and the interpolation, add keyframes and gradients, and even add image masks. You can learn more about the animation properties in CSS on MDN.

Using the View Transitions API for MPAs

As mentioned, view transitions also work for MPAs with no client-side routing. Setting up view transitions for MPAs is much easier than for SPAs. All you have to do is include the CSS rule on all pages where you want the animations.

@view-transition {
    navigation: auto;
}

Of course, like how it works with SPAs, you can customize the animations to your heart’s content using the view transition pseudo-elements.

Conclusion

I hope this article helped you understand the fundamentals of using the View Transitions API. Now that most browsers support it and it’s not too difficult to add it to existing applications, it is a great time to learn how to use it.

You can view a full list of examples here (made by Jake Archibald and Bramus) if you want to see how advanced the animations can get. I like the Star Wars ones especially. The README explains how to get all the examples up and running (a total of 43!).

This Dot is a consultancy dedicated to guiding companies through their modernization and digital transformation journeys. Specializing in replatforming, modernizing, and launching new initiatives, we stand out by taking true ownership of your engineering projects.

We love helping teams with projects that have missed their deadlines or helping keep your strategic digital initiatives on course. Check out our case studies and our clients that trust us with their engineering.

Let's innovate together!

We're ready to be your trusted technical partners in your digital innovation journey.

Whether it's modernization or custom software solutions, our team of experts can guide you through best practices and how to build scalable, performant software that lasts.

Prefer email? hi@thisdot.co