A zero-dependency carousel with drag-to-scroll, auto-play, and accessible navigation built on CSS scroll-snap.
Preview
Installation
Usage
import {
Carousel,
CarouselItem,
CarouselPrevious,
CarouselNext,
CarouselDots,
} from "@/components/ui/carousel"
export default function Example() {
return (
<Carousel>
<CarouselItem>Slide 1</CarouselItem>
<CarouselItem>Slide 2</CarouselItem>
<CarouselItem>Slide 3</CarouselItem>
<CarouselPrevious />
<CarouselNext />
<CarouselDots />
</Carousel>
)
}Examples
With Progress Bar
1/5
<Carousel>
{items.map((item) => (
<CarouselItem key={item.id}>{item.content}</CarouselItem>
))}
<CarouselPrevious />
<CarouselNext />
<CarouselProgress />
<CarouselCounter />
</Carousel>Number Indicators
<CarouselDots variant="numbers" />Line Indicators
<CarouselDots variant="line" />Multiple Items
<CarouselItem size="1/3">
{/* Shows 3 items at once */}
</CarouselItem>Auto-Play with Loop
<Carousel autoPlay={3000} loop>
{/* Auto-advances every 3 seconds */}
</Carousel>Vertical
<Carousel orientation="vertical" className="h-[400px]">
{/* Vertical scrolling */}
</Carousel>Features
- CSS Scroll Snap — Native scroll snapping for smooth, performant scrolling (96.4% browser support)
- Drag to Scroll — Mouse drag support with click prevention after dragging
- Keyboard Navigation — Arrow keys for prev/next slide
- Auto-Play — Optional auto-advance with pause on hover
- Loop — Optional infinite loop mode
- Multiple Indicators — Dots, lines, numbers, progress bar, counter
- Accessible — ARIA roles, labels, and keyboard support
- Vertical Mode — Supports both horizontal and vertical orientation
Props
Carousel
| Prop | Type | Default | Description |
|---|---|---|---|
| orientation | "horizontal" | "vertical" | "horizontal" | Scroll direction |
| draggable | boolean | true | Enable drag-to-scroll |
| loop | boolean | false | Loop to start/end |
| autoPlay | number | 0 | Auto-play interval in ms |
| pauseOnHover | boolean | true | Pause auto-play on hover |
CarouselItem
| Prop | Type | Default | Description |
|---|---|---|---|
| size | "full" | "1/2" | "1/3" | "1/4" | "auto" | "full" | Item width |
CarouselDots
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "dots" | "line" | "numbers" | "dots" | Indicator style |
useCarousel Hook
Access carousel state and controls programmatically:
import { useCarousel } from "@/components/ui/carousel"
function CustomControls() {
const {
scrollPrev,
scrollNext,
scrollTo,
activeIndex,
itemCount,
canScrollPrev,
canScrollNext,
} = useCarousel()
return (
<div>
<button onClick={scrollPrev} disabled={!canScrollPrev}>
Previous
</button>
<span>{activeIndex + 1} / {itemCount}</span>
<button onClick={scrollNext} disabled={!canScrollNext}>
Next
</button>
</div>
)
}Implementation Notes
This carousel uses CSS Scroll Snap for native scroll snapping with JavaScript for navigation controls. This approach ensures 96.4% browser support while providing smooth, performant scrolling.
CSS Features Used
scroll-snap-type: x mandatory— Snap alignmentsnap-start— Item snap pointsoverscroll-behavior: contain— Prevent page scroll
Future CSS Carousels (Chrome 135+)
New CSS features like ::scroll-button, ::scroll-marker, and scroll-state() queries enable pure-CSS carousels but are not yet Baseline.