View Transition API FTW!
What is View Transition API ?
I don't remember the last time I was this excited for a browser feature, One thing that the browser lacked is a way to transitions between pages. Each new page is a new document and by the time the new page is loaded the old one is discarded. If you are using a Single Page Application you can transition between "pages" because the SPAs actually only changes the route of the application but the DOM structure remains in place.
Luckily with this new browser feature (which works only on SPAs for now) you have a very easy and reliable way to create animations when the structure of the document object model changes. The idea is very simple: Before discarding the old document the browser takes a snapshot (literally a screenshot) of your page and then computes a new snapshot with the new document, then it does it's best to morph those two snapshots together. By default you have a simple fade in/out animation but you can tell the browser to separate elements in their own snapshot and then animate them independently.
Let's see how it works:
I created a small page here with some image thumbnails, clicking on a thumbnail would open that image for a better view, something like this:
The transitions between the two states is, well, non existent. We could add some fade-in/out animations but we can actually let the heavy lifting to the view transition API:
Click handler to open the image view:
I won't show all the code because it's written in react and it really is out of scope, this is a browser feature not a React feature which means it should work with your preferred framework/UI Library
const onThumbnailClick = () => {
// check if the browser supports view transitions
if(document.startViewTransition) {
// if there is a thumbnail with the transition class
// we need to remove it to avoid colisions
const prevImage = document.querySelector('.img-wrapper.transition');
if(prevImage) prevImage.classList.remove('transition');
// same for the text element
const prevText = document.querySelector('.img-wrapper .text.transition');
if(prevText) prevText.classList.remove('transition');
// now add the transition class to the current clicked element
ev.currentTarget.classList.add('transition');
const text = ev.currentTarget.querySelector<HTMLParagraphElement>('.text');
//and it's text child
if(text) text.classList.add('transition');
// tell the browser to initiate the transition
document.startViewTransition(() => {
// this is needed for react to update the state syncronously
flushSync(() => {
setOpenImage(i);
});
});
} else {
setOpenImage(i);
}
}The transition CSS class just contains the view-transition-name necessary for the API to create the snapshots, and we need to toggle it from the previous thumbnail to the new one:
.image.transition {
view-transition-name: image-hero;
}
.text.transition {
view-transition-name: image-title;
}Click handler to open close the image view:
const onBackClick = () => {
if(document.startViewTransition) {
document.startViewTransition(() => {
flushSync(() => {
setOpenImage(-1);
})
});
} else {
setOpenImage(-1);
}
}Now if we have a look at the outcome we can see that we have a really nice animation going:
With View Transition:
It is amazing how much of an impact it makes with such little code, if we had to do that animation ourselves it would have been really painful.
Unfortunately this only works with Single Page Applications, there is still work in progress with the transition of actual html pages but given the excitement of the community on this feature I am confident we'll get there in no time.
if you want to take a more in depth here are some useful links: