logoImgdanvudi
ContactWorksQuizBlog

Cookie Settings

TitleExpirationDescription

We use cookies.

We use cookies for Google Ads and Analytics to improve tracking. Read cookie policy.

Contact Privacy Policy +1 (920) 213-4617

Follow Along

by Shon Danvudi.

4/6/2025

Creating a Seamless Marquee in React with Framer Motion

imageBlog

This tutorial will guide you through the steps to implement a seamless looping marquee effect in React using Framer Motion (Motion Dev).

1. Building an Infinite Marquee

Building an Infinite Marquee

Framer Motion  (now Motion Dev) does lack examples on the web. The library itself has been out for quite some time, but has undergone many updates to the website documentation. This often gets developers stuck and leads to the replacement of the framework with other libraries, which also often creates other complications.
This tutorial will guide you through the steps to implement a seamless looping marquee effect for your React application. I am going to be using Next.js 15 and app router, but the implementation should work with any React framework.
//MarqueeItem.tsx
How this works is pretty simple. Assuming we have an array of strings that we would like to loop horizontally and infinitely. Depending on the number of items in the array, we need to manually adjust the duration value. Even though there are two <motion.div> this is a single marquee. Once the first div ends, the second one follows it right after to create an infinite marquee illusion. Simple, flawless, infinite.
import { motion } from "framer-motion";
import Image from "next/image";

export default function MarqueeItem({images, from, to}:{ images:string[], 
from:number | string, to:number | string }){
  return (
    <div className="flex my-24">
      <motion.div
        initial={{ x: `${from}` }}
        animate={{ x: `${to}` }}
        transition={{ duration: 60, repeat: Infinity, ease: "linear" }}
        className="flex flex-shrink-0"
      >
        {images.map((image:any, index:number) => {
          return <Image width={200} height={200} alt={`marqueImg${index}`}
             className="object-contain pr-20" src={image} key={index} />
        })}
      </motion.div>

      <motion.div
        initial={{ x: `${from}` }}
        animate={{ x: `${to}` }}
        transition={{ duration: 60, repeat: Infinity, ease: "linear" }}
        className="flex flex-shrink-0"
      >
        {images.map((image:any, index:number) => {
          return <Image width={200} height={200} alt={`marqueImg${index}`} 
            className="object-contain pr-20" src={image} key={index} />;
        })}
      </motion.div>
    </div>
  );
};
Now, to call this component, I created another component called Marquee.tsx. This isn’t necessary because we can call the MarqueeItem.tsx anywhere in the app as long as it contains “use client” since {motion} can only run on the client.
import MarqueeItem from "./MarqueeItem";

export default function Marquee() {

  const images = [
    "/imageOne.webp",
    "/imageTwo.webp",
    "/imageThree.webp",
    "/imageFour.webp",
    "/imageFive.webp",
    "/imageSix.webp",
    "/imageSeven.webp"
  ];

  return (
    <div className="container mx-auto overflow-hidden select-none">
      <MarqueeItem images={images} from={0} to={"-100%"} />
    </div>
  );
};

Tags