Animating your SPA with the new View Transitions API

Animating your SPA with the new View Transitions API
Photo: Midjourney
Lasse Schou

Lasse Schou

25 March 2023

As of this month, View Transitions API is stable in Chrome and Edge, giving web developers an easy and lightweight way of animating page transitions in Single Page Applications (SPAs).

Mobile apps have been using animated transitions like this for years, not only improving the visual experience but also helping users stay in context and not be confused about a sudden page transition.

We've enabled View Transitions in our app. As you can see below, the top and left bar stays in their place, while the page content fades and slides in:

If you already have an SPA, it's incredibly simple to implement, but as with any new technology, there are caveats to be aware of. In this blog I'll explain how we implemented View Transitions.

Before View Transitions API

Before View Transitions API it was cumbersome and challenging to implement animated page transitions. Figuring out the position of the elements of the previous page vs. the new isn't trivial, especially when also dealing with scroll, back/forward navigation, and making everything degrade gracefully if browsers don't support it. JavaScript libraries that make this somewhat easier - like Highway JS do exist - but it looks like they are no longer needed.

Getting started with the first transition - the fade

const spaNavigate = content => {

// Fallback for browsers that don't support View Transitions API:
if (!document.startViewTransition) {
updateDOM(content);
return;
}
document.startViewTransition(_ => updateDOM(content));
}

This is enough to get you started with the default transition: cross-fade.

Let's see how that looks:

Customizing the animation and naming areas

This was actually a great first take. But our goal was to make the page content slide in from the left. This CSS is the same that Material Design 's Shared Axis is using:

@keyframes fade-in { from { opacity: 0; }}
@keyframes fade-out { to { opacity: 0; }}
@keyframes slide-from-left { from { transform: translateX(-30px); }}
@keyframes slide-to-right { to { transform: translateX(30px); }}

::view-transition-old(root) {
animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-right;
}

::view-transition-new(root) {
animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-left;
}

This is how things looked after adding that CSS:

Ok, it's clear that the left and top bar have to be fixed somehow. The solution is to give those sections a view-transition-name and then not attaching any of the view-transition pseudo classes to them:

.topbar {
...
view-transition-name: topbar;
}

.sidebar {
...
view-transition-name: sidebar;
}

That's it. The transition now looks great:

Animations cause flicker

One issue that we faced was that CSS animations would cause the view transitions to flicker and ruin the whole experience. You can see how it looked here:

After investigating this for a while, we came up with a solution that simply pauses all CSS animations during the view transition.

You need to await the finished promise on the object returned by the document.startViewTransition call, so remember the async keyword on your method:

const spaNavigate = async (content) => {

// Fallback for browsers that don't support View Transitions API:
if (!document.startViewTransition) {
updateDOM(content);
return;
}

// Pause CSS animations
const stylesheet = document.styleSheets[0];
const cssRule = stylesheet.insertRule("*, *::after { animation-play-state: paused!important }");

const viewTransition = document.startViewTransition(_ => updateDOM(content));

try {
await viewTransition.finished;
} finally {
// Unpause CSS animations
document.styleSheets[0].deleteRule(0);
}
}

Alright, let's see how that looks:

Somewhat more complex code, but stopping the animations was enough to ensure silky smooth transitions on pages with CSS animations. Nota that if your pages have complex CSS transitions, you may encounter some of the same problems.

Next step: non-SPAs

The Chrome team is currently working on bringing View Transitions API to regular web pages where each navigation is a full page reload.

If you enable chrome://flags/#document-transition you'll be able to see an early preview of how this will work. We tried implementing page transitions on this very website (go try for yourself), but from what we can see, it's still very early days. Some transitions work, some change behavior on each navigation, some cause flicker. But overall we believe this is a very interesting feature which could mean less dependence on large JS frameworks like Angular and React.

Want to take PageVitals for a spin?

Page speed monitoring and alerting for your website. Get daily Lighthouse reports for all your pages. No installation needed.

Start my free trial