1rem
3.5rem

Infinite Image Carousel

A high-performance infinite carousel with deep interaction handling: mathematically precise looping (even with 2 images), hover slowdown, drag with inertia, and a prop-based logo layout mode.

0px
Live Playground · Edit and see changes instantly
Or edit with AI support by
import {InfiniteImageCarousel} from "@/components/phucbm/infinite-image-carousel";

export default function Example() {
    return (
        <div className="h-screen flex flex-col justify-around py-[10vh]">
            {/*logos*/}
            <InfiniteImageCarousel
                itemClass="lg:mr-8 lg:px-4 xl:[--width:12vw] md:[--width:150px] [--width:100px]"
                isLogo={true}
                direction={1}
                images={exampleLogos()}
            />

            {/*photos*/}
            <InfiniteImageCarousel
                itemClass="xl:[--width:15vw] lg:[--width:260px] [--width:120px]"
                images={examplePhotos()}/>
        </div>
    );
}

const examplePhotos = () => Array.from({length: 10}, (_, i) => ({
    url: `https://picsum.photos/400/400?random=${Math.random()}`,
    title: `Image ${i + 1}`
}));
const exampleLogos = () => {
    const brands = ['apple', 'google', 'microsoft', 'meta', 'netflix', 'spotify', 'samsung', 'intel', 'adobe'];
    const shuffled = brands.sort(() => Math.random() - 0.5);
    return shuffled.map((brand, i) => ({
        url: `https://cdn.svglogos.dev/logos/${brand}.svg`,
        title: brand.charAt(0).toUpperCase() + brand.slice(1)
    }));
};

Installation

pnpm dlx shadcn@latest add @phucbm/infinite-image-carousel

Props

NameTypeDefault
images{ url: string; title?: string; }[]

List of image objects to display in the carousel. Each must contain a valid url property.

itemClassstring

Custom class for each item wrapper, useful when you want to tweak the size or gap. Example: xl:[--width:15vw] lg:[--width:260px] [--width:120px]

imageClassstring

Optional custom class name applied to the image element.

durationnumber

Duration in seconds for one complete loop of images at normal speed. Lower = faster.

20
hoverDurationnumber

Duration in seconds for one complete loop when hovering. Lower = faster.

60
direction1 | -1

Scroll direction of the carousel. -1 = scrolls right→left, 1 = scrolls left→right.

-1
dragboolean

Whether to enable drag/swipe interactions.

true
hoverboolean

Whether to enable hover slowdown behavior.

true
scrollboolean

Whether to enable scroll-based speed control.

false
scrollSensitivitynumber

Adjust this value to control sensitivity (lower = more sensitive).

200

Examples

Sync with your scroll speed

Set a slower base duration so the scroll speed changes are more noticeable.

<InfiniteImageCarousel scroll={true} scrollSensitivity={200} duration={40} hoverDuration={70}/>
0px
Live Playground · Edit and see changes instantly
Or edit with AI support by
import {InfiniteImageCarousel} from "@/components/phucbm/infinite-image-carousel";

export default function InfiniteImageCarousel_ScrollExample() {
    return (
        <div className="flex flex-col justify-between py-[15vh]">

            <p className="container mx-auto px-6 max-w-xl md:text-xl font-medium font-mono mb-[15vh]">
                Two rows of logos that can be dragged, they scroll faster when you scroll (in either direction) and
                slow down on hover.
            </p>

            {/*logos*/}
            <div className="relative
                before:absolute before:inset-y-0 before:left-0 before:w-[clamp(20px,20vw,300px)] before:bg-gradient-to-r before:from-white before:to-transparent before:z-20 before:pointer-events-none
                after:absolute after:inset-y-0 after:right-0 after:w-[clamp(20px,20vw,300px)] after:bg-gradient-to-l after:from-white after:to-transparent after:z-20 after:pointer-events-none
                ">
                <InfiniteImageCarousel
                    itemClass="lg:mr-8 lg:px-4 2xl:[--width:160px] md:[--width:120px] [--width:80px]"
                    isLogo={true}
                    direction={1}
                    images={exampleLogos()}
                    scroll={true}
                    scrollSensitivity={200}
                    duration={40}
                    hoverDuration={70}
                />
            </div>
            <div className="relative
                before:absolute before:inset-y-0 before:left-0 before:w-[clamp(20px,20vw,300px)] before:bg-gradient-to-r before:from-white before:to-transparent before:z-20 before:pointer-events-none
                after:absolute after:inset-y-0 after:right-0 after:w-[clamp(20px,20vw,300px)] after:bg-gradient-to-l after:from-white after:to-transparent after:z-20 after:pointer-events-none
                ">
                <InfiniteImageCarousel
                    itemClass="lg:mr-8 lg:px-4 2xl:[--width:160px] md:[--width:120px] [--width:80px]"
                    isLogo={true}
                    direction={-1}
                    images={exampleLogos()}
                    scroll={true}
                    scrollSensitivity={200}
                    duration={40}
                    hoverDuration={70}
                />
            </div>

            <p className="container mx-auto px-6 max-w-xl mt-[15vh] md:text-xl font-medium font-mono">
                Lorem ipsum dolor sit amet dis nec mollis maximus fames. Sapien pede proin amet ut dictum. Cras
                habitasse
                a volutpat id non felis parturient orci elementum accumsan praesent. Potenti sem augue primis fusce
                nisi.
                Urna aliquam ultrices nunc praesent tellus sapien auctor aptent ipsum vel metus. Ligula sagittis mus
                tristique netus ex ac. Lectus rhoncus proin aliquet eros efficitur quam sed.

                Litora quis iaculis integer si ultrices aenean. Est sodales ridiculus metus cras diam ante imperdiet
                curabitur. Tristique et per fames amet elit habitasse sit. Facilisis torquent hac sodales lorem euismod
                vulputate mus purus. Si tristique ligula class diam cursus sociosqu risus ullamcorper imperdiet mus
                vitae. Mauris dis sem penatibus urna tempus laoreet. Vivamus consectetuer et leo fringilla proin donec.
            </p>

        </div>
    );
}

const exampleLogos = () => {
    const brands = ['apple', 'google', 'microsoft', 'meta', 'netflix', 'spotify', 'samsung', 'intel', 'adobe'];
    const shuffled = brands.sort(() => Math.random() - 0.5);
    return shuffled.map((brand, i) => ({
        url: `https://cdn.svglogos.dev/logos/${brand}.svg`,
        title: brand.charAt(0).toUpperCase() + brand.slice(1)
    }));
};
3.5rem
1rem