Carousel

A horizontally (or vertically) scrolling set of slides — a thin Tidal wrapper around embla-carousel-react following the shadcn composition. Reach for it when a set of peer items (media, feature cards, testimonials) is too wide to show at once and browsing is exploratory rather than ordered. Prefer a plain grid or wrapped flex layout when every item should be visible at once, and Tabs or Pagination when users navigate to a specific, addressable item. Set orientation to horizontal (default) for galleries and card rails, or vertical for stacked feeds in a fixed-height column. Use basis-* on CarouselItem to show one slide at a time (full-bleed hero media) or several per view (compact card rails). The Previous/Next controls are md outline IconButtons by default — the standalone control size; drop to sm only inside dense, inline rails.

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