Pontus Horn

View transitions in Astro and SvelteKit

Table of contents

The flagship feature in Astro 3 is support for the new View Transitions API. Exciting! SvelteKit also shipped support for the API, and I’ve had a chance to try both. While I’m certainly no expert on the subject, I can compare and contrast their approaches from a first-impression standpoint. Please let me know if I’ve misrepresented or omitted something!

Do view transitions even need to be “supported”?

I’ll admit that my first reaction on hearing of these frameworks “supporting” view transitions was skeptical. If it’s a web API, why would frameworks need to support them? Surely they’re already supported when the browser supports them!

But the current state of browser support, where it exists, is for what’s called “single-document” transitions - in other words, we can only use them when JavaScript is the thing updating the DOM. Support for cross-document view transitions (ie. regular old hyperlinks) is in the works.

Until then, if we want to use view transitions for page navigation, we need a client-side router that calls document.startViewTransition and updates the DOM in the callback passed to it. That’s where the need for framework “support” comes in.

How frameworks support view transitions

In Astro, you enable view transitions by including a <ViewTransitions /> component in the head of the page or pages where you want them. Astro did not previously use a client-side router, so this new component adds one for you, enables view transitions in it, and adds a fallback behaviour for browsers that don’t yet support the API. Astro then provides directives (spicy attributes) like transition:name or transition:animate to customize how transitions are applied, as well as some preset animations you can opt in to.

In SvelteKit’s case, there was already a client-side router available, but its API had previously made it difficult to hook in and call startViewTransition in the right way. Their approach to supporting view transitions is simply to add a new lifecycle hook called onNavigate. The hook is not inherently related to view transitions, but what it does is offer you the exactly right place to slot in the call to startViewTransition. And that’s it! Once you’ve done that, the rest is up to you and the browser APIs. So even though both frameworks have unlocked the API, their approaches are quite different.

SvelteKit’s approach

SvelteKit is - at least so far - very unopinionated on how you use view transitions. In fact, they say as much in their introductory blog post (same as the one linked above):

As you can see, SvelteKit doesn’t abstract a whole lot about how view transitions work – you’re interacting directly with the browser’s built-in document.startViewTransition and ::view-transition APIs, rather than framework abstractions like those found in Nuxt and Astro. We’re eager to see how people end up using view transitions in SvelteKit apps, and whether it makes sense to add higher level abstractions of our own in future.

In other words, their support of the API is about as barebones as it gets. On the surface of it, this might seem a bit stingy and low-effort, but to me this feels like a very sober approach.

SvelteKit does not hold your hand here or give you any convenience features, but it’s also not limiting you in any way - you have free rein to use the platform API as it’s designed. They’re also opening for adding comfortable abstractions in the future, as they learn more about how people use them.

Astro’s approach

Astro comes at it from a whole other angle - they give you bells and whistles, and a very integrated flavour to the API. In fact, as far as I can tell, you never interact with the native view transitions API in Astro at all - it’s all done through Astro APIs.

I suppose that fact is what lets the framework provide some guarantees about cross-browser support and so on, which is nice. But is the abstraction they provide valuable enough to justify a whole new API on top of the browser’s?

Ease of use

In both Astro and SvelteKit, I found the learning curve to be quite similar. Enabling the default full-page crossfade mode of view transitions was a breeze in both. Making a few basic tweaks, like making an element that’s present in both pages move seamlessly into place - easy!

When trying to do anything more advanced than that, I found it got finicky pretty fast. But I think that mostly comes down to me not having a good intuition yet for when the platform defaults have my back, and when I as a developer need to step in and guide the transitions.

What about other frameworks?

I’m definitely not well versed here, but based on some quick research it looks like Nuxt supports view transitions in a flavour of their own, whereas Next.js seems to lack support for them as of yet.

My thoughts so far

If it wasn’t clear before, I think SvelteKit made the better move here. Adding the bare minimum support might not sound very good on the surface, but it shows some trust for the platform - and I think it’s very early to start providing abstractions for platform APIs that have barely seen any use yet.

Astro does let you specify custom animations, but I wish it let me escape its abstractions a bit easier. For example, it might be a good idea that their default behaviour for reduced motion preferences is to disable animations - but reduced motion doesn’t mean no motion, and I might want to provide sensible alternatives for this case. For now I can’t find a way to do so.

It is of course early days, so take this with a grain of salt. I’m sure Astro has plenty of improvements lined up to solve issues like the above. But one benefit of SvelteKit’s more hands-off approach is that we don’t have to be at their mercy for that sort of thing.

It also means that the skills I learn are transferable. If I were to become an expert on using view transitions in a SvelteKit app, I could likely apply that expertise in other frameworks too, or when not using a framework at all. Less so with Astro. Not only that, but my code also becomes more portable, as specifying my view transitions in regular CSS rather than through framework-specific directives means they will probably just keep working if I take my code elsewhere.

I hope Astro proves me wrong here, but in my experience the best abstractions grow iteratively based on observing the pain points of using the layer below. They also don’t stop you from dipping down into that lower layer as needed. However, the team behind Astro have certainly done a good job in many other places and it’s perfectly possible that most of my concerns will be addressed or turn out not to be big issues. For now, I’m just thankful we have this fantastic new API at all. The web certainly feels a bit more magical for it.