DEV Community

Adrien Poly
Adrien Poly

Posted on

Animations with Turbolinks and Stimulus

When using CSS animations with Turbolinks, you may experience page blink during page load. We will see how we can easily change that using the life cycle events of a Stimulus controller.

Turbolinks preview

When navigating back, Turbolinks will first display a preview of the page that was stored in the local cache and then replace this preview by the server response. This give the instant page load feeling effect of Turbolinks.

This preview comes from a snapshot taken locally by Turbolinks during the previous visit. Two things must be considered :

  1. The preview has the state of the animations when you left the page.
  2. If your animations are meant to start on page load then they will most probably start when the preview page is displayed

Those are typically two sources of blink effect when using animations with Turbolinks.

The first one because the page preview displays the page with the animations already played but then resets it when the preview is replaced by the real server response.

The second because if the animations start on the page preview then once the page preview is replaced, the animation will be reset. So either the animation is played twice or partially during first load.

Solution

Lets assume a typical controller where you play an animation on an element during after page load.

export default class extends Controller {
  connect() {
    this.element.classList.add("play-animation")
  }
}
Enter fullscreen mode Exit fullscreen mode

1) Ignore preview

Adding simple helper can tell you if you are on a preview page on not

get isPreview() {
  return document.documentElement.hasAttribute("data-turbolinks-preview");
}
Enter fullscreen mode Exit fullscreen mode

So now during connect we can easily know if we are in a preview or not. If we are in a preview we shouldn't play the animation as it would most likely be interrupted/play twice

export default class extends Controller {
  connect() {
    if (!this.isPreview) {
      this.element.classList.add('play-animation')
    }
  }

  get isPreview() {
    return document.documentElement.hasAttribute('data-turbolinks-preview')
  }
}
Enter fullscreen mode Exit fullscreen mode

2) Tear down before snapshot

The goal is to bring back the page to its initial state before snapshot. For this we will use the disconnect() function that occurs right before the Turbolinks snapshot and simply remove animations that we may have played.

disconnect() {
  this.element.classList.remove('play-animation')
}
Enter fullscreen mode Exit fullscreen mode

So a typical complete solution would look like this

export default class extends Controller {
  connect() {
    if (!this.isPreview) {
      this.element.classList.add('play-animation')
    }
  }

  disconnect() {
    this.element.classList.remove('play-animation')
  }

  get isPreview() {
    return document.documentElement.hasAttribute('data-turbolinks-preview')
  }
}
Enter fullscreen mode Exit fullscreen mode

Happy Stimulus πŸ¦„

Top comments (2)

Collapse
 
skatkov profile image
Stanislav(Stas) Katkov

Hey Adrien,

Do you know that you can create Pull Request to github.com/skatkov/awesome-stimulusjs with articles like that ? ;-)

Collapse
 
adrienpoly profile image
Adrien Poly

Yeah will do thanks for the proposal