Carousel
Image and content slider — supports auto-play, navigation arrows, dot indicators, and smooth slide transitions.
Preview
Basic carousel
import { Carousel, CarouselSlide } from '@/registry/ui-kit/tailwind-carousel/react'
function Example() {
return (
<Carousel>
<CarouselSlide>
<div className="flex h-48 items-center justify-center bg-indigo-100">
<span className="text-lg font-semibold text-indigo-600">Slide 1</span>
</div>
</CarouselSlide>
<CarouselSlide>
<div className="flex h-48 items-center justify-center bg-emerald-100">
<span className="text-lg font-semibold text-emerald-600">Slide 2</span>
</div>
</CarouselSlide>
</Carousel>
)
}Auto-play
import { Carousel, CarouselSlide } from '@/registry/ui-kit/tailwind-carousel/react'
function Example() {
return (
<Carousel autoPlay interval={3000}>
<CarouselSlide><div className="h-48 bg-rose-100" /></CarouselSlide>
<CarouselSlide><div className="h-48 bg-sky-100" /></CarouselSlide>
<CarouselSlide><div className="h-48 bg-violet-100" /></CarouselSlide>
</Carousel>
)
}Component API
| Prop | Default | Description |
|---|---|---|
autoPlay | false | Automatically cycle through slides. |
interval | 5000 | Auto-play interval in milliseconds. |
className | "" | Additional CSS classes for the carousel wrapper. |
CarouselSlide | — | Wrapper for each individual slide. |
Source Code
"use client";
import { useState, useEffect, useCallback, Children } from "react";
/* ── CarouselSlide ── */
interface CarouselSlideProps {
className?: string;
children: React.ReactNode;
}
function CarouselSlide({ className = "", children }: CarouselSlideProps) {
return <div className={`min-w-full ${className}`}>{children}</div>;
}
/* ── Carousel ── */
interface CarouselProps {
autoPlay?: boolean;
interval?: number;
className?: string;
children: React.ReactNode;
}
function Carousel({ autoPlay = false, interval = 5000, className = "", children }: CarouselProps) {
const slides = Children.toArray(children);
const count = slides.length;
const [activeIndex, setActiveIndex] = useState(0);
const next = useCallback(() => {
setActiveIndex((prev) => (prev + 1) % count);
}, [count]);
const prev = useCallback(() => {
setActiveIndex((prev) => (prev - 1 + count) % count);
}, [count]);
useEffect(() => {
if (!autoPlay) return;
const timer = setInterval(next, interval);
return () => clearInterval(timer);
}, [autoPlay, interval, next]);
return (
<div className={`relative overflow-hidden rounded-lg ${className}`}>
{/* Slides */}
<div
className="flex transition-transform duration-500"
style={{ transform: `translateX(-${activeIndex * 100}%)` }}
>
{slides}
</div>
{/* Left arrow */}
{count > 1 && (
<button
type="button"
onClick={prev}
className="absolute left-3 top-1/2 -translate-y-1/2 rounded-full bg-black/30 p-1.5 text-white backdrop-blur-sm transition-colors hover:bg-black/50"
aria-label="Previous slide"
>
<svg
className="h-4 w-4"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="m15 18-6-6 6-6" />
</svg>
</button>
)}
{/* Right arrow */}
{count > 1 && (
<button
type="button"
onClick={next}
className="absolute right-3 top-1/2 -translate-y-1/2 rounded-full bg-black/30 p-1.5 text-white backdrop-blur-sm transition-colors hover:bg-black/50"
aria-label="Next slide"
>
<svg
className="h-4 w-4"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="m9 18 6-6-6-6" />
</svg>
</button>
)}
{/* Indicators */}
{count > 1 && (
<div className="absolute bottom-3 left-1/2 flex -translate-x-1/2 gap-1.5">
{slides.map((_, i) => (
<button
key={i}
type="button"
onClick={() => setActiveIndex(i)}
className={`h-2 w-2 rounded-full transition-colors ${
i === activeIndex ? "bg-white" : "bg-white/50"
}`}
aria-label={`Go to slide ${i + 1}`}
/>
))}
</div>
)}
</div>
);
}
export { Carousel, CarouselSlide };
export default Carousel;