Search code examples
reactjsnext.jsframer-motionnext.js14

Next.js 14: Framer Motion useScroll with element container not updating


I'm using Next.js v14.2.5 and Framer Motion v11.3.21. I'm working on a card carousel that scrolls horizontally. To track the scrolling progress, I'm using the useScroll hook from Framer Motion. However, I'm facing an issue where the useScroll hook is not detecting the scrolling or updating the scrolling value correctly.

Heres my code

'use client'
import { useRef, useState } from 'react';
import { useScroll, useTransform, motion, useMotionValueEvent } from "framer-motion";

export default function Home() {
  const carouselRef = useRef(null);
  const [progress, setPorgress] = useState(0);

  const { scrollXProgress } = useScroll({
    container: carouselRef
  });
  const background = useTransform(scrollXProgress, [0, 1], ['rgb(255, 255, 255)', 'rgb(220, 0, 0)']);
  
  return (
    <motion.main className='h-full flex items-center overflow-x-scroll snap-x snap-mandatory' style={{ background }}>
      <div ref={carouselRef} className='flex gap-4'>
        <div className='bg-red-500 h-96 rounded-md w-[calc(100vw_-_2rem)] mx-4 p-4 text-white snap-center shadow-lg'>
          <h1>Card</h1>
          { progress }
         </div>
        <div className='bg-blue-500 h-96 rounded-md w-[calc(100vw_-_2rem)] mx-4 p-4 text-white snap-center shadow-lg'>
          <h1>Card</h1>
        </div>
        <div className='bg-green-500 h-96 rounded-md w-[calc(100vw_-_2rem)] mx-4 p-4 text-white snap-center shadow-lg'>
          <h1>Card</h1>
        </div>
      </div>
    </motion.main>
  );
}

Im using useTrasform in order to see the change in scrolling progress visually, but instead the progress values seems to be always equal to 1

How can I track the scrolling progress value correctly ?


Solution

  • The useScroll hook needs to be attached to the scrolling element, which in your case is the motion.main element. However, you're passing the carouselRef as the container option, but you need to pass any ref's as the target option.

    For useScroll do this:

      const { scrollXProgress } = useScroll({
        target: carouselRef
      });
    

    For your JSX do this:

    return (
      <motion.main ref={scrollRef}>
        {/* ur other carousel content's */}
      </motion.main>
    );
    

    Additionally, you might want to consider using useMotionValue instead of useTransform to get the scroll progress value, as it's a more straightforward way to get the current value of a motion value.

    const scrollXProgress = useMotionValue();
    

    This should help you. I hope...