CLICK
← Docs

Reset GSAP ScrollSmoother
after Barba transition

Used technologies:

The problem

After implementing a basic GSAP ScrollSmoother instance you'll notice that after a Barba page transition the page won't scroll back to the top or won't scroll at all.
That is because Barba has shown a new container over the current one and didn't refresh after the new content appeared.
In the Barba lifecycle in between the `afterLeave` and `afterEnter` events 2 page containers live in the DOM together.

2 barba containers

Hiding next Barba container

Before the next container is shown I hide it so the DOM won't contain 2 containers and thereby increasing the height of the body. The next container will be overlapping the current one but will be hidden. As so:

gsap.set(data.next.container, { position: "fixed", inset: "0", zIndex: "-1" });

This can be reset by clearing the set props (assuming it doesnt contain any self-created inline styling)

gsap.set(data.next.container, {clearProps: 'all'});

Killing ScrollSmoother

The target here is to reset the ScrollSmoother instance, by scrolling to the top, killing it and recreating it.
I have seen multiple implementations of killing all ScrollTrigger instances but that won't be enough.

Because ScrollSmoother can only be created once you have to get that instance first before using it, as followed:

var sm = ScrollSmoother.get();
sm.scrollTo(0);
sm.kill();

Resetting ScrollTrigger

IF you are using ScrollTrigger's in your GSAP animations it is also useful to reset those.
Recreating them on the next container is fine but the previous ScrollTrigger instances won't be deleted so a lot of instances will be created or duplicated.
To delete those, I use this snippet:

let triggers = ScrollTrigger.getAll();
triggers.forEach( trigger => {
  trigger.kill();
});

Final result

Putting it all together:

import barba from '@barba/core';
import { gsap, ScrollTrigger, ScrollSmoother } from "./gsap.js";

barba.init({
  prevent: ({ el, href }) => href == '#',
  transitions: [{
    beforeEnter(data) {
      const done = this.async();
      
      // 1. animate current container and disappear
      gsap.to(data.current.container, {
        opacity: 0,
        duration: 0.3,
        onComplete: () => {
          gsap.set(data.next.container, { position: "fixed", inset: "0", zIndex: "-1" }); // hide next container behind current one
          data.current.container.remove();

          // Remove all GSAP Scroll triggers or we get 'em duplicated
          let triggers = ScrollTrigger.getAll();
          triggers.forEach( trigger => {
            trigger.kill();
          });

          // Get the current ScrollSmoother instance and 'reset' it
          var sm = ScrollSmoother.get();
          sm.scrollTo(0);
          sm.kill();

          // Recreating the ScrollSmoother
          ScrollSmoother.create({
            smooth: 1,               // how long (in seconds) it takes to "catch up" to the native scroll position
            effects: true,           // looks for data-speed and data-lag attributes on elements
            smoothTouch: 0.1,        // much shorter smoothing time on touch devices (default is NO smoothing on touch devices)
          });

          done();
        }
      });
    },
    enter(data) {
      gsap.set(data.next.container, {clearProps: 'all'}); // remove z-index from next container (previously added)
    },
    afterEnter(data) {
      const done = this.async();

      // load init javascript here before content is loaded
      
      // // 4. show the next container fading in and trigger next event
      gsap.to(data.next.container, {
        opacity: 1,
        duration: 0.7,
        onComplete: function() {
          done();
        },
      });
    }
  }]
});  


Contact_

Let's get in touch

And we'll see from there

Collaboration?
Start a project?
You name it...


Contact me at

hello@luukthe.dev