1

I am trying to use locomotive scroll but but I get an error

enter image description here

I try to use react-locomotive-scroll but get the same error

My _app.tsx looks like this

enter image description here

Where is my mistake? I try to put inside layout component which wraps the entire page, but it didn't work too

import type { AppProps } from "next/app";
import { ThemeProvider, Global } from "@emotion/react";
import { theme } from "../src/common/theme";
import { globalStyles } from "../src/styles/global";
import { appWithTranslation } from "next-i18next";
import "locomotive-scroll/dist/locomotive-scroll.css";
import { useEffect } from "react";

function MyApp({ Component, pageProps }: AppProps) {
  useEffect(() => {
    let scroll;
    import("locomotive-scroll").then((locomotiveModule) => {
      scroll = new locomotiveModule.default({
        el: document.querySelector("[data-scroll-container]"),
        smooth: true,
        smoothMobile: false,
        resetNativeScroll: true,
      });
    });

    // `useEffect`'s cleanup phase
    return () => scroll.destroy();
  });

  return (
    <ThemeProvider theme={theme}>
      <Global styles={globalStyles(theme)} />
      <Component {...pageProps} data-scroll-container />
    </ThemeProvider>
  );
}

export default appWithTranslation(MyApp);

I also use next-transpile-modules

const withTM = require("next-transpile-modules")(["gsap", "locomotive-scroll"]);
juliomalves
  • 42,130
  • 20
  • 150
  • 146
PrinceLoren
  • 81
  • 1
  • 6

3 Answers3

2

The error occurs because no element with the data-scroll-container attribute is present on the DOM.

Setting data-scroll-container in Component doesn't actually add the attribute to any element. Component is a React component, not an HTML element.

You should add a new element that wraps Component and set the attribute on it instead.

<div data-scroll-container>
    <Component {...pageProps} />
</div>
juliomalves
  • 42,130
  • 20
  • 150
  • 146
0

What worked for me was doing this:

useEffect(() => {
  ;(async () => {
    try {
      const LocomotiveScroll = (await import('locomotive-scroll')).default
      const dataScrollContainer = document.querySelector(
        '[data-scroll-container]',
      )

      if (!dataScrollContainer) {
        console.warn(
          'locomotive-scroll: [data-scroll-container] dataset was not found. You likely forgot to add it which will prevent Locomotive Scroll to work.',
        )
      }

      window.locomotive = new LocomotiveScroll({
        el: dataScrollContainer ?? undefined,
        smooth: true,
      })
    } catch (error) {}
  })()

  return () => {
    window.locomotive?.destroy()
  }
}, [])

And have a div with data-scroll-container somewhere. So instead of directly loading it, do it async.

0

Can I suggest a simpler option that worked for me:


export default function Component() {

  const { scroll } = useLocomotiveScroll()

  useEffect(() => {
    if (scroll) {
      scroll.on('scroll', onScroll);
    }
    // remove event on unmount to prevent a memory leak with the cleanup
    return () => {
      if (scroll) {
        scroll.destroy('scroll', onScroll);
      }
    }
  }, [scroll]);

};

Just provide the locomotive scroll as a dependency to t useEffect.

useEffect(() => {...}, [scroll]) // See scroll used as a dependency here

Problem solved.