Jump to content
Search Community

Recommended Posts

Hi, i'm trying to create an horizontal scroll on my gallery section.
So i followed lot of tutorials, and compared my code to lot of codepen to understand why, my horizontal scroll doesn't works.
Sometimes it works, but the section is not pinned,
Sometimes the section is pinned but scroll doesn't works.

What did i do wrong.

Also i'm using tailwindCss.

Here is my useLocoScroll Hook:

import {gsap} from "gsap"
import { ScrollTrigger } from "gsap/dist/ScrollTrigger"
import LocomotiveScroll from "locomotive-scroll"
import "locomotive-scroll/src/locomotive-scroll.scss"
import { useEffect } from "react"


const useLocoScroll = () => {
        const scrollEl = document.querySelector('#page-wrapper');

        let locoScroll = new LocomotiveScroll({
            el: scrollEl,
            class: 'is-reveal',
        locoScroll.on('scroll', () => ScrollTrigger.update)
        ScrollTrigger.scrollerProxy(scrollEl, {
            scrollTop(value) {
              if (locoScroll) {
                return arguments.length
                  ? locoScroll.scrollTo(value, 0, 0)
                  : locoScroll.scroll.instance.scroll.y;
              return null;
            getBoundingClientRect() {
                return {top: 0, left: 0, width: window.innerWidth, height: window.innerHeight};
            pinType: scrollEl.style.transform ? "transform" : "fixed"
        const lsUpdate = () => {
            if (locoScroll) {

        ScrollTrigger.addEventListener("refresh", lsUpdate);

        return () => {
            if (locoScroll) {
              ScrollTrigger.removeEventListener("refresh", lsUpdate);
              locoScroll = null;

export default useLocoScroll

And here my gallery component.

import gsap from "gsap";
import { useEffect, useRef } from "react";

const Gallery = ({ data }) => {
    const ref = useRef(null);
    const projects = data.projects.data

    useEffect(() => {
        // This does not seem to work without a settimeout
        setTimeout(() => {
            let sections = gsap.utils.toArray(".gallery-item-wrapper");
            const sectionWidth= ref.current.offsetWidth
            const scrollWidth = sectionWidth - window.innerWidth
            console.log(ref.current.offsetWidth - window.innerWidth);

          gsap.to(sections, {
            scrollTrigger: {
                scroller: "#page-wrapper",
                scrub: true,
                trigger: ".sectionHorizontalScroll",
                pin: true,
                snap: 1 / (sections.length - 1),
                start: "top top",
                end: ()=> `+=${sectionWidth}`,
                markers: {startColor: "green", endColor: "red", fontSize: "12px"}
            x: -100 * (sections.length - 1),
            ease: "none",
      }, []);

        <section id="" className="h-screen w-max py-32 sectionHorizontalScroll" data-scroll-section >
            <div className="pinSectionHorizontalScroll">
                <div className="flex flex-nowrap w-max" ref={ref} >
                            projects.map((project, index) => {
                                return <GalleryItem 
                                    key={project.attributes.title + "-" + index}

const GalleryItem = ({mignature, title}) => {
    return (
        <div className="w-[50vh] gallery-item-wrapper" data-scroll>
            <h3 className="text-3xl font-bold text-grayPrimary">{title}</h3>

export default Gallery

At last, i added my locoScroll to my Layout like that:

import ImageWebP from '../../components/ImageWebP'
import Birds from './Canvas/Birds'
import { Outlet } from "react-router-dom";
import Header from './Header';
import { useState } from 'react';
import CursorProvider from './CustomCursor';
import useLocoScroll from '../../services/useLocoScroll';

export default function Layout() {

    const [isMenuOpen, toggleMenuOpen] = useState(false)
                <Header toggleMenu={toggleMenuOpen} isMenu={isMenuOpen}/>
                <section id='page' 
                        'h-full w-full relative  transition-all' +
                        (isMenuOpen ? " ml-96" : " ml-0")
                    <section className='fixed left-0 top-0 w-screen h-screen -z-10'>
                        src={window.location.origin + "/assets/imgs/backrounds/background1.webp"}
                        fallback={window.location.origin + "/assets/imgs/backrounds/background1.jpg"}
                        classnames=" h-screen w-screen object-screen block "
                        alt="A photo showing the expiration date on a box of Lucky Charms" 
                        className="object-cover h-full w-full"
                    <Birds />
                    <div id='page-wrapper' className=" w-full h-full min-h-screen z-30" data-scroll-container>
                        <Outlet />

Thank you if you take times to help me.

There is the demo here:  https://justhugo.fr


See the Pen by (@) on CodePen

Link to comment
Share on other sites

It's pretty tough to troubleshoot without a minimal demo - the issue could be caused by CSS, markup, a third party library, your browser, an external script that's totally unrelated to GSAP, etc. Would you please provide a very simple CodePen or CodeSandbox that demonstrates the issue? 


Please don't include your whole project. Just some colored <div> elements and the GSAP code is best (avoid frameworks if possible). See if you can recreate the issue with as few dependancies as possible. If not, incrementally add code bit by bit until it breaks. Usually people solve their own issues during this process! If not, then at least we have a reduced test case which greatly increases your chances of getting a relevant answer.


Here's a starter CodePen that loads all the plugins. Just click "fork" at the bottom right and make your minimal demo

See the Pen aYYOdN by GreenSock (@GreenSock) on CodePen


If you're using something like React/Next/Vue/Nuxt or some other framework, you may find StackBlitz easier to use. We have a series of collections with different templates for you to get started on these different frameworks: React/Next/Vue/Nuxt.


Once we see an isolated demo, we'll do our best to jump in and help with your GSAP-specific questions. 

Link to comment
Share on other sites

Hi @JustHugo welcome to the forum! 


Sorry we can't really debug live websites, there is just no way to modify the code. Try creating a simple version of what you're trying to do, this has two benefits. First this allows you to experiment and try out new ideas. By making it simple people usually solve 90% of their own bugs. Second, you have an easy version you can share in which anyone could edit and modify the code. If you're using React, just fork the StackBlitz template and you're off to the races.


Also be sure to read this post to be up to date how to use GSAP with React, there are some gotchas!



  • Like 1
Link to comment
Share on other sites



Sorry about the frustration, we all know is hard trying to solve something and not being able to progress on it.


Unfortunately locomotive is not a GSAP plugin and we can’t provide a lot of support for it. I’ve never used locomotive so I can’t say what could be the issue you’re having right now.


 I could be wrong about this, but maybe the problem stems from the way pinning works in ScrollTrigger and the way locomotive works in moving the scroller container. Take a look at this for better understanding:



 On top of that is Saturday night and, while we thrive in giving all our users the best and fastest possible support we do ask for understanding if there is some sort of latency on the weekend.


 Here is one example of using locomotive with an horizontal section 

See the Pen KKoZxVL by kairij (@kairij) on CodePen


 Also you can find some results looking in google:


 Hopefully this helps.

 Happy Tweening!

Link to comment
Share on other sites

I don't have time to dig into this and I would definitely recommend ScrollSmoother over LocomotiveScroll primarily because it's integrated with GSAP and fully supported in these forums (sorry, we didn't author LocomotiveScroll, so we can't support it here). A few things I'll mention in case they're helpful:

  • I think LocomotiveScroll changed their API in version 4: 
    // OLD
    locoScroll.scrollTo(value, 0, 0)
    // NEW
    locoScroll.scrollTo(value, {duration: 0, disableLerp: true})

    (you have the old one in your useLocoScroll file)

  • You aren't doing proper cleanup. React 18 will call your useEffect()/useLayoutEffect() TWICE, so you must do proper cleanup to ensure you're not accidentally creating multiple conflicting animations/ScrollTriggers. This is why gsap.context() is so valuable. @mvaneijgen suggested you read that React article and it's all over that, so I'd strongly recommend making sure you read that article and follow the advice. 
  • We typically recommend using useLayoutEffect() instead of useEffect(). 
  • I think you're missing an empty dependency Array in your useLocoScroll useEffect().

In case it helps, here's an implementation that's linked to in our docs (not using React): 

See the Pen zYrELYe by GreenSock (@GreenSock) on CodePen

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Create New...