import { Card, CardContent } from "@/components/ui/card";
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel";
export function CarouselDemo() {
return (
<Carousel className="w-full sm:max-w-xs">
<CarouselContent>
{Array.from({ length: 5 }).map((_, index) => (
<CarouselItem key={index}>
<div className="p-1">
<Card>
<CardContent className="flex aspect-square items-center justify-center p-6">
<span className="font-semibold text-4xl">{index + 1}</span>
</CardContent>
</Card>
</div>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
);
}
pnpm dlx shadcn@latest add https://herocn.dev/r/carousel.jsonimport {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel"<Carousel>
<CarouselContent>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>Use the following composition to build a Carousel:
Carousel
├── CarouselContent
│ ├── CarouselItem
│ └── CarouselItem
├── CarouselPrevious
└── CarouselNextTo set the size of the items, you can use the basis utility class on the <CarouselItem />.
import { Card, CardContent } from "@/components/ui/card";
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel";
export function CarouselSize() {
return (
<Carousel
opts={{
align: "start",
}}
className="w-full sm:max-w-xs md:max-w-sm"
>
<CarouselContent>
{Array.from({ length: 5 }).map((_, index) => (
<CarouselItem key={index} className="basis-1/2 lg:basis-1/3">
<div className="p-1">
<Card>
<CardContent className="flex aspect-square items-center justify-center p-6">
<span className="font-semibold text-3xl">{index + 1}</span>
</CardContent>
</Card>
</div>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
);
}
// 33% of the carousel width.
<Carousel>
<CarouselContent>
<CarouselItem className="basis-1/3">...</CarouselItem>
<CarouselItem className="basis-1/3">...</CarouselItem>
<CarouselItem className="basis-1/3">...</CarouselItem>
</CarouselContent>
</Carousel>// 50% on small screens and 33% on larger screens.
<Carousel>
<CarouselContent>
<CarouselItem className="md:basis-1/2 lg:basis-1/3">...</CarouselItem>
<CarouselItem className="md:basis-1/2 lg:basis-1/3">...</CarouselItem>
<CarouselItem className="md:basis-1/2 lg:basis-1/3">...</CarouselItem>
</CarouselContent>
</Carousel>To set the spacing between the items, use a pl-[VALUE] utility on the <CarouselItem /> and a negative -ml-[VALUE] on the <CarouselContent />.
import { Card, CardContent } from "@/components/ui/card";
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel";
export function CarouselSpacing() {
return (
<Carousel className="w-full sm:max-w-xs md:max-w-sm">
<CarouselContent className="-ml-1">
{Array.from({ length: 5 }).map((_, index) => (
<CarouselItem key={index} className="basis-1/2 pl-1 lg:basis-1/3">
<div className="p-1">
<Card>
<CardContent className="flex aspect-square items-center justify-center p-6">
<span className="font-semibold text-2xl">{index + 1}</span>
</CardContent>
</Card>
</div>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
);
}
<Carousel>
<CarouselContent className="-ml-4">
<CarouselItem className="pl-4">...</CarouselItem>
<CarouselItem className="pl-4">...</CarouselItem>
<CarouselItem className="pl-4">...</CarouselItem>
</CarouselContent>
</Carousel><Carousel>
<CarouselContent className="-ml-2 md:-ml-4">
<CarouselItem className="pl-2 md:pl-4">...</CarouselItem>
<CarouselItem className="pl-2 md:pl-4">...</CarouselItem>
<CarouselItem className="pl-2 md:pl-4">...</CarouselItem>
</CarouselContent>
</Carousel>Use the orientation prop to set the orientation of the carousel.
import { Card, CardContent } from "@/components/ui/card";
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel";
export function CarouselOrientation() {
return (
<Carousel
opts={{
align: "start",
}}
orientation="vertical"
className="w-full sm:max-w-xs"
>
<CarouselContent className="-mt-1 h-[270px]">
{Array.from({ length: 5 }).map((_, index) => (
<CarouselItem key={index} className="basis-1/2 pt-1">
<div className="p-1">
<Card>
<CardContent className="flex items-center justify-center p-6">
<span className="font-semibold text-3xl">{index + 1}</span>
</CardContent>
</Card>
</div>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
);
}
<Carousel orientation="vertical | horizontal">
<CarouselContent>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
</CarouselContent>
</Carousel>You can pass options to the carousel using the opts prop. See the Embla Carousel docs for more information.
<Carousel
opts={{
align: "start",
loop: true,
}}
>
<CarouselContent>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
</CarouselContent>
</Carousel>Use a state and the setApi prop to get an instance of the carousel API.
"use client";
import * as React from "react";
import { Card, CardContent } from "@/components/ui/card";
import {
Carousel,
type CarouselApi,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel";
export function CarouselApiExample() {
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 (
<div className="mx-auto max-w-96 sm:max-w-xs">
<Carousel setApi={setApi} className="w-full max-w-xs">
<CarouselContent>
{Array.from({ length: 5 }).map((_, index) => (
<CarouselItem key={index}>
<Card>
<CardContent className="flex aspect-square items-center justify-center p-6">
<span className="font-semibold text-4xl">{index + 1}</span>
</CardContent>
</Card>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
<div className="py-2 text-center text-muted-foreground text-sm">
Slide {current} of {count}
</div>
</div>
);
}
You can listen to events using the api instance from setApi.
import { type CarouselApi } from "@/components/ui/carousel"
export function Example() {
const [api, setApi] = React.useState<CarouselApi>()
React.useEffect(() => {
if (!api) {
return
}
api.on("select", () => {
// Do something on select.
})
}, [api])
return (
<Carousel setApi={setApi}>
<CarouselContent>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
</CarouselContent>
</Carousel>
)
}Use the plugins prop to add plugins to the carousel.
"use client";
import Autoplay from "embla-carousel-autoplay";
import * as React from "react";
import { Card, CardContent } from "@/components/ui/card";
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel";
export function CarouselPlugin() {
const plugin = React.useRef(
Autoplay({ delay: 2000, stopOnInteraction: true }),
);
return (
<Carousel
plugins={[plugin.current]}
className="w-full sm:max-w-xs"
onMouseEnter={plugin.current.stop}
onMouseLeave={plugin.current.reset}
>
<CarouselContent>
{Array.from({ length: 5 }).map((_, index) => (
<CarouselItem key={index}>
<div className="p-1">
<Card>
<CardContent className="flex aspect-square items-center justify-center p-6">
<span className="font-semibold text-4xl">{index + 1}</span>
</CardContent>
</Card>
</div>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
);
}
import Autoplay from "embla-carousel-autoplay"
export function Example() {
return (
<Carousel
plugins={[
Autoplay({
delay: 2000,
}),
]}
>
...
</Carousel>
)
}See the Embla Carousel docs for more information on using events.
To enable RTL support in shadcn/ui, see the RTL configuration guide.
"use client";
import {
type Translations,
useTranslation,
} from "@/components/language-selector";
import { Card, CardContent } from "@/components/ui/card";
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel";
const translations: Translations = {
en: {
dir: "ltr",
values: {},
},
ar: {
dir: "rtl",
values: {},
},
he: {
dir: "rtl",
values: {},
},
};
function toArabicNumerals(num: number): string {
const arabicNumerals = ["٠", "١", "٢", "٣", "٤", "٥", "٦", "٧", "٨", "٩"];
return num
.toString()
.split("")
.map((digit) => arabicNumerals[Number.parseInt(digit, 10)])
.join("");
}
export function CarouselRtl() {
const { dir, language } = useTranslation(translations, "ar");
const formatNumber = (num: number): string => {
if (language === "ar") {
return toArabicNumerals(num);
}
return num.toString();
};
return (
<Carousel
dir={dir}
className="w-full sm:max-w-xs"
opts={{
direction: dir,
}}
>
<CarouselContent>
{Array.from({ length: 5 }).map((_, index) => (
<CarouselItem key={index}>
<div className="p-1">
<Card>
<CardContent className="flex aspect-square items-center justify-center p-6">
<span className="font-semibold text-4xl">
{formatNumber(index + 1)}
</span>
</CardContent>
</Card>
</div>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
);
}
When localizing the carousel for RTL languages, set the direction option in the opts prop to match the text direction.
<Carousel
dir={dir}
opts={{
direction: dir,
}}
>
<CarouselContent>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
</CarouselContent>
<CarouselPrevious className="rtl:rotate-180" />
<CarouselNext className="rtl:rotate-180" />
</Carousel>The direction option accepts "ltr" or "rtl" and should match the dir prop value. The navigation buttons include rtl:rotate-180 by default to ensure they point in the correct direction.
The carousel is built on Embla Carousel. See the Embla Carousel API docs for more information on props, events, and plugins.
| Prop | Type | Default |
|---|---|---|
| opts | CarouselOptions | — |
| plugins | CarouselPlugin | — |
| orientation | "horizontal" | "vertical" | "horizontal" |
| setApi | (api: CarouselApi) => void | — |