Native view transitions in Chrome with Turbo is awesome (and has a small bug)

Hi guys,

Something awesome dropped in Chrome 111 this month – View Transitions!

View transitions lets us create an animations, where an element on one page morphs into an element on another page at the click of a link.

Currently it only works in SPAs, but if you are using Turbo you are in luck!


To get started with this, I recommend you go read How to use View Transitions in Hotwire Turbo by Matouš Borák.


Personally I ended up using this JavaScript code:

addEventListener("turbo:before-render", (event: TurboBeforeRenderEvent) => {
  if (document.startViewTransition) {
    event.preventDefault();

    document.startViewTransition(() => {
      event.detail.resume();
    });
  }
});

And then just tagging elements that are the same on multiple pages using css:

#an-element {
  viewTransitionName: header-image-element;
}

The bug

When navigating by history, Turbo uses the cache. For some reason JavaScript is re-applied when loading a page from cache (and using JavaScript-snippet above).

This goes for both my Stimulus-controllers and other JavaScript.

The temporary solution for the bug

The hack to fix the bug for now was to turn off the page cache on all pages.

<meta name="turbo-cache-control" content="no-cache">
2 Likes

I haven’t played with the page transitions features yet; but, I’ve read that Turbo Drive adds [data-turbo-preview] to the document when a Preview is being rendered. Perhaps you could use that to disable the CSS selector, like:

html[ data-turbo-preview ] * {
    view-transition-name: none ! important ;
}

That way, you could still have the caching in place, but disable the transitions… maybe.

I don’t think it’s the css that is the problem. This issue is JavaScript-related.

I’m wondering of startViewTransition() does something to the scope so that Turbo loses it’s context in some way.

Ahh, very possibly. I just thought maybe if the browser couldn’t find any elements to actually transition then maybe you’d be back in the clear.

Here is an issue around this on Github: