Command Palette

Search for a command to run...

Infinite Slider

Infinite scrolling slider component that smoothly loops through its children. It supports both horizontal and vertical directions, with customizable speed and speed on hover. Ideal for creating continuous carousels, marquee displays, or dynamic content showcases.

Apple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logo

Installation

Install the following dependencies:

pnpm add motion

Create a file at components/motion/infinite-slider.tsx and paste the given code.

"use client";
import { cn } from "@/lib/utils";
import { useMotionValue, animate, motion } from "motion/react";
import { useState, useEffect } from "react";
import useMeasure from "react-use-measure";
 
export type InfiniteSliderProps = {
    children: React.ReactNode;
    gap?: number;
    speed?: number;
    speedOnHover?: number;
    direction?: "horizontal" | "vertical";
    reverse?: boolean;
    className?: string;
};
 
export function InfiniteSlider({
    children,
    gap = 16,
    speed = 100,
    speedOnHover,
    direction = "horizontal",
    reverse = false,
    className,
}: InfiniteSliderProps) {
    const [currentSpeed, setCurrentSpeed] = useState(speed);
    const [ref, { width, height }] = useMeasure();
    const translation = useMotionValue(0);
    const [isTransitioning, setIsTransitioning] = useState(false);
    const [key, setKey] = useState(0);
 
    useEffect(() => {
        let controls;
        const size = direction === "horizontal" ? width : height;
        const contentSize = size + gap;
        const from = reverse ? -contentSize / 2 : 0;
        const to = reverse ? 0 : -contentSize / 2;
 
        const distanceToTravel = Math.abs(to - from);
        const duration = distanceToTravel / currentSpeed;
 
        if (isTransitioning) {
            const remainingDistance = Math.abs(translation.get() - to);
            const transitionDuration = remainingDistance / currentSpeed;
 
            controls = animate(translation, [translation.get(), to], {
                ease: "linear",
                duration: transitionDuration,
                onComplete: () => {
                    setIsTransitioning(false);
                    setKey((prevKey) => prevKey + 1);
                },
            });
        } else {
            controls = animate(translation, [from, to], {
                ease: "linear",
                duration: duration,
                repeat: Infinity,
                repeatType: "loop",
                repeatDelay: 0,
                onRepeat: () => {
                    translation.set(from);
                },
            });
        }
 
        return controls?.stop;
    }, [
        key,
        translation,
        currentSpeed,
        width,
        height,
        gap,
        isTransitioning,
        direction,
        reverse,
    ]);
 
    const hoverProps = speedOnHover
        ? {
              onHoverStart: () => {
                  setIsTransitioning(true);
                  setCurrentSpeed(speedOnHover);
              },
              onHoverEnd: () => {
                  setIsTransitioning(true);
                  setCurrentSpeed(speed);
              },
          }
        : {};
 
    return (
        <div className={cn("overflow-hidden", className)}>
            <motion.div
                className="flex w-max"
                style={{
                    ...(direction === "horizontal"
                        ? { x: translation }
                        : { y: translation }),
                    gap: `${gap}px`,
                    flexDirection:
                        direction === "horizontal" ? "row" : "column",
                }}
                ref={ref}
                {...hoverProps}
            >
                {children}
                {children}
            </motion.div>
        </div>
    );
}

Update the import paths to match your project setup.

Examples

Infinite Slider

Apple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logo

Vertical Infinite Slider

Apple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logo
Apple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logoApple Music logo