- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Autocomplete
- Avatar
- Badge
- Bar List
- Breadcrumb
- Button
- Button Group
- Calendar
- Card
- Carousel
- Checkbox
- Checkbox Group
- Circular Progress
- Collapsible
- Combobox
- Command
- Context Menu
- Currency Input
- Data Table
- Date Picker
- Dialog
- Drawer
- Dropdown Menu
- Empty
- Field
- Hover Card
- Input
- Input Group
- Input OTP
- Item
- Kbd
- Label
- Menubar
- Meter
- Multi Combobox
- Native Select
- Navigation Menu
- Number Field
- Pagination
- Phone Input
- Popover
- Progress
- Progress List
- Prompt
- Radio Group
- Resizable
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Spinner
- Stat
- Switch
- Table
- Tabs
- Textarea
- Toggle
- Toggle Group
- Tooltip
- Typography
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 max-w-[12rem] sm:max-w-xs">
<CarouselContent>
{Array.from({ length: 5 }).map((_, index) => (
<CarouselItem key={index}>
<div className="p-1">
<Card>
<CardContent className="flex aspect-16/9 items-center justify-center p-6">
<span className="font-semibold text-4xl">{index + 1}</span>
</CardContent>
</Card>
</div>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
);
}About
The carousel component is built using the Embla Carousel library.
Installation
npx shadcn@latest add "https://ui.blode.co/r/styles/default/carousel"
Usage
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel";<Carousel>
<CarouselContent>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>Examples
Sizes
To 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
className="w-full max-w-[12rem] sm:max-w-xs md:max-w-sm"
opts={{
align: "start",
}}
>
<CarouselContent>
{Array.from({ length: 5 }).map((_, index) => (
<CarouselItem className="basis-1/2 lg:basis-1/3" key={index}>
<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>Spacing
To set the spacing between the items, we use a pl-[VALUE] utility on the <CarouselItem /> and a negative -ml-[VALUE] on the <CarouselContent />.
Why: I tried to use the gap property or a grid layout on the <CarouselContent /> but it required a lot of math and mental effort to get the
spacing right. I found pl-[VALUE] and -ml-[VALUE] utilities much easier to
use.
You can always adjust this in your own project if you need to.
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 max-w-[12rem] sm:max-w-xs md:max-w-sm">
<CarouselContent className="-ml-1">
{Array.from({ length: 5 }).map((_, index) => (
<CarouselItem className="basis-1/2 pl-1 lg:basis-1/3" key={index}>
<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>Orientation
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
className="w-full max-w-xs"
opts={{
align: "start",
}}
orientation="vertical"
>
<CarouselContent className="-mt-1 h-[270px]">
{Array.from({ length: 5 }).map((_, index) => (
<CarouselItem className="basis-1/2 pt-1" key={index}>
<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>Options
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>API
Use a state and the setApi props to get an instance of the carousel API.
"use client";
import { useEffect, useState } from "react";
import { Card, CardContent } from "@/components/ui/card";
import {
Carousel,
type CarouselApi,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel";
export function CarouselDApiDemo() {
const [api, setApi] = useState<CarouselApi>();
const [current, setCurrent] = useState(0);
const [count, setCount] = useState(0);
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-[10rem] sm:max-w-xs">
<Carousel className="w-full max-w-xs" setApi={setApi}>
<CarouselContent>
{Array.from({ length: 5 }).map((_, index) => (
<CarouselItem key={index}>
<Card className="m-px">
<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>
);
}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>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
</CarouselContent>
</Carousel>
);
}Events
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>
);
}See the Embla Carousel docs for more information on using events.
Plugins
You can use the plugins prop to add plugins to the carousel.
import Autoplay from "embla-carousel-autoplay"
export function Example() {
return (
<Carousel
plugins={[
Autoplay({
delay: 2000,
}),
]}
>
// ...
</Carousel>
)
}"use client";
import Autoplay from "embla-carousel-autoplay";
import { useRef } from "react";
import { Card, CardContent } from "@/components/ui/card";
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel";
export function CarouselPlugin() {
const plugin = useRef(Autoplay({ delay: 2000, stopOnInteraction: true }));
return (
<Carousel
className="w-full max-w-[10rem] sm:max-w-xs"
onMouseEnter={plugin.current.stop}
onMouseLeave={plugin.current.reset}
plugins={[plugin.current]}
>
<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>
);
}See the Embla Carousel docs for more information on using plugins.