Skip to content

Instantly share code, notes, and snippets.

@gursuj
Last active August 17, 2025 15:25
Show Gist options
  • Select an option

  • Save gursuj/ddf503e36072b9d987faa82f43290f78 to your computer and use it in GitHub Desktop.

Select an option

Save gursuj/ddf503e36072b9d987faa82f43290f78 to your computer and use it in GitHub Desktop.
Fix for when ScrollTrigger is broken due to layout shift caused by lazy loading

On Firefox, lazy loaded images may break GSAP ScrollTrigger positions. LLMs will propbably advise you to call ScrollTrigger.refresh() after every lazyloaded image finishes loading.

A better way is to call refresh when a scrollTrigger (either a ScrollTrigger close to the image, or set a trigger on the image itself) is triggered using the onEnter lifecycle method. This is done to re-calculate start, end positions for all triggers after the image has loaded and caused a layout shift.

const node = document.querySelector('...');
node.isRefreshCalled = false;

gsap.to(node, {
    scrollTrigger: {
        trigger: node,
        start: 'top top',
        end: 'max',
        onEnter: () => {
            if (!node.isRefreshCalled) {
                ScrollTrigger.refresh();
                node.isRefreshCalled = true;
            }
        },
    }
}

Here, we set isRefreshCalled so that refresh is called only when the trigger is first entered (and hopefully all images have loaded). This is to prevent unnecessary refreshes if the user scrolls up and down. You may want to call refresh for every trigger, if needed.

You may also want to call refresh when the viewport is resized (resizing browser on desktop, rotating on mobile).

// debounced refresh
let resizeTimeout;
window.addEventListener("resize", () => {
    clearTimeout(resizeTimeout);
    resizeTimeout = setTimeout(() => {
        ScrollTrigger.refresh();
    }, 250);
});

I guess this code could probably be extra-optimized to always use debounce when refreshing instead of calling it directly, but eh. Probably better to not delay refresh in onEnter since doing so may cause visual bugs for a short while before the timeout ends.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment