Carousel
A touch-friendly content slider with previous/next navigation. Supports horizontal and vertical orientations, multi-slide views, and looping. Built on Embla Carousel.
Anatomy
<Carousel> is the root — pass opts (Embla options), plugins, and orientation here. CarouselContent is the slide track. Each slide is a CarouselItem — use Tailwind basis-* classes on it to control how many slides are visible at once. CarouselPrevious and CarouselNext are the arrow buttons.
1
2
3
4
5
CarouselContent (slide track)CarouselItem (one slide)CarouselPrevious / CarouselNext
Examples
Multi-slide — two visible at once
A
B
C
D
E
F
Looping — with loop option
Red
Green
Blue
Purple
Vertical orientation
Slide 1
Slide 2
Slide 3
Slide 4
Design Guidelines
Do
- Use for browsing sequences. Carousels work best for visually similar, ordered content — images, testimonials, product cards — where the user actively wants to browse.
- Show partial next slide. Peeking the edge of the next slide signals that there's more content to scroll — it's a natural affordance for swiping.
- Use basis-* for multi-slide layouts. Set
basis-1/2orbasis-1/3onCarouselItemto show multiple slides at once without extra configuration.
Don't
- Don't auto-play without user consent. Auto-advancing carousels disorient users and violate WCAG 2.1 (motion). If you need it, provide pause controls.
- Don't use for critical content. Users miss content in carousels. Important information should be always visible, not hidden behind a next button.
- Don't add too many slides. Beyond 5–7 items the carousel becomes tedious to navigate. Consider a grid layout instead for large collections.
Developer Reference
Accessibility & Behavior
- The
<Carousel>root renders withrole="region"andaria-roledescription="carousel". EachCarouselItemhasrole="group"andaria-roledescription="slide". - Arrow keys (Left/Right or Up/Down for vertical) navigate between slides when the carousel is focused.
CarouselPreviousandCarouselNextare automatically disabled when there are no more slides to scroll (unlessopts.loopis enabled).- Access the Embla API via the
setApiprop to imperatively control scroll position, listen to events, or build custom indicators. optsaccepts any Embla Carousel option:loop,align,skipSnaps,dragFree, etc.
Usage
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselPrevious,
CarouselNext,
} from "@/components/ui/carousel"
// Basic carousel
<Carousel>
<CarouselContent>
{items.map((item) => (
<CarouselItem key={item.id}>
<div className="aspect-square rounded-lg bg-muted">{item.label}</div>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
// Multi-slide: two per view
<Carousel>
<CarouselContent>
{items.map((item) => (
<CarouselItem key={item.id} className="basis-1/2">
{item.label}
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
// Looping
<Carousel opts={{ loop: true }}>...</Carousel>
// Vertical
<Carousel orientation="vertical">...</Carousel>
// Access Embla API
const [api, setApi] = useState<CarouselApi>()
<Carousel setApi={setApi}>...</Carousel>