Carousel

A horizontally (or vertically) scrolling set of slides. Thin Tidal wrapper around embla-carousel-react, following the shadcn composition.

Horizontal (default)

1
2
3
4
5
<Carousel opts={{ align: "start" }}>
  <CarouselContent>
    {items.map((i) => (
      <CarouselItem key={i}>
        <Slide>{i}</Slide>
      </CarouselItem>
    ))}
  </CarouselContent>
  <CarouselPrevious />
  <CarouselNext />
</Carousel>

Multiple items per view

1
2
3
4
5
6
7
8
<Carousel opts={{ align: "start" }}>
  <CarouselContent>
    {items.map((i) => (
      <CarouselItem key={i} className="basis-1/3">
        <Slide>{i}</Slide>
      </CarouselItem>
    ))}
  </CarouselContent>
  <CarouselPrevious />
  <CarouselNext />
</Carousel>

Vertical

1
2
3
4
5
<Carousel orientation="vertical" opts={{ align: "start" }}>
  <CarouselContent className="h-[260px]">
    {items.map((i) => (
      <CarouselItem key={i} className="basis-1/2">
        <Slide>{i}</Slide>
      </CarouselItem>
    ))}
  </CarouselContent>
  <CarouselPrevious />
  <CarouselNext />
</Carousel>

Reading the API

1
2
3
4
5

Slide 1 of 0

const [api, setApi] = React.useState<CarouselApi>();
const [current, setCurrent] = React.useState(1);
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]);

<Carousel setApi={setApi}>…</Carousel>
<p>Slide {current} of {count}</p>

Props

ComponentNotable props
Carouselorientation ("horizontal" | "vertical"), opts (embla options), plugins, setApi
CarouselContentStandard div props. Holds CarouselItems, receives embla viewport ref internally.
CarouselItemStandard div props. basis-* controls per-view slide count.
CarouselPreviousIconButton props. Defaults to variant="outline", size="md".
CarouselNextIconButton props. Defaults to variant="outline", size="md".

Source

packages/react/src/components/carousel.tsx · Figma node 2819:21463