Carousel
A carousel with motion and swipe built using the Embla Carousel library.
Last updated January 1, 1980
Copied from shadcn/ui.
- Replaced the multi-icon-library IconPlaceholder abstraction with plain lucide-react chevron icons
- Replaced cn-rtl-flip with rtl:-scale-x-100 on the horizontal navigation icons, and the physical -left-12/-right-12 button offsets with logical -start-12/-end-12 so horizontal navigation mirrors in RTL; vertical centering stays physical so the buttons hold their position in RTL
- The carousel now inherits the ambient writing direction automatically instead of defaulting to LTR; opts.direction still overrides it
Overview
Installation
Usage
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel"
<Carousel>
<CarouselContent>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>Sizes
To set the size of the items, use the basis utility class on the <CarouselItem />.
Spacing
Set the spacing between items with a pl-[VALUE] utility on <CarouselItem /> and a negative -ml-[VALUE] on <CarouselContent />.
Orientation
Use the orientation prop to set the orientation of the carousel.
Options
Pass Embla options through the opts prop — see Embla's option list.
<Carousel
opts={{
align: "start",
loop: true,
}}
>
<CarouselContent>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
</CarouselContent>
</Carousel>API & Events
Use a state and the setApi prop to get an instance of the carousel API, then listen to Embla events like select on it.
import { type CarouselApi } from "@/components/ui/carousel"
export function Example() {
const [api, setApi] = React.useState<CarouselApi>()
const [current, setCurrent] = React.useState(0)
const [count, setCount] = React.useState(0)
React.useEffect(() => {
if (!api) {
return
}
setCount(api.scrollSnapList().length)
setCurrent(api.selectedScrollSnap() + 1)
api.on("select", () => {
setCurrent(api.selectedScrollSnap() + 1)
})
}, [api])
return (
<Carousel setApi={setApi}>
<CarouselContent>{/* items */}</CarouselContent>
</Carousel>
)
}Plugins
Use the plugins prop to add Embla plugins such as Autoplay.
RTL
The carousel inherits the surrounding writing direction automatically, so it scrolls the right way under dir="rtl" with no extra props — and the navigation buttons mirror via logical start/end offsets plus rtl:-scale-x-100 on their chevrons. To pin a direction explicitly regardless of the document, pass it through opts.
// Inherits dir from the document automatically.
<Carousel>
<CarouselContent>{/* items */}</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
// Or pin a direction explicitly:
<Carousel opts={{ direction: "rtl" }}>
<CarouselContent>{/* items */}</CarouselContent>
<CarouselPrevious className="rtl:-scale-x-100" />
<CarouselNext className="rtl:-scale-x-100" />
</Carousel>