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 :
- The preview has the state of the animations when you left the page.
- 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")
}
}
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");
}
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')
}
}
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')
}
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')
}
}
Happy Stimulus 🦄
Top comments (2)
Hey Adrien,
Do you know that you can create Pull Request to github.com/skatkov/awesome-stimulusjs with articles like that ? ;-)
Yeah will do thanks for the proposal