Carousel

Slide and snap content with keyboard, drag, and pointer support, built on Embla Carousel.

1
2
3
4
5
<Carousel className="w-full">  <CarouselContent>    {Array.from({ length: 5 }).map((_, index) => (      <CarouselItem key={index} className="md:basis-1/2 lg:basis-1/3">        <Card>          <CardContent className="flex aspect-square items-center justify-center p-6">            <Heading as="span" size="3xl" weight="semibold">              {index + 1}            </Heading>          </CardContent>        </Card>      </CarouselItem>    ))}  </CarouselContent>  <CarouselPrevious />  <CarouselNext /></Carousel>

Usage

import {
  Carousel,
  CarouselContent,
  CarouselItem,
  CarouselNext,
  CarouselPrevious,
} from "@reva/ui";

Carousel is built on Embla Carousel and uses Reva's IconButton for CarouselPrevious / CarouselNext (the upstream shadcn size="icon-sm" is mapped to IconButton size="sm"). Pass Embla options via opts, register plugins via plugins, and switch axis with orientation="vertical".

Composition

Use the following composition to build a Carousel:

Carousel
├── CarouselContent
│   ├── CarouselItem
│   ├── CarouselItem
│   └── CarouselItem
├── CarouselPrevious
└── CarouselNext

Examples

Sizes

Set per-slide width with basis-* utilities on CarouselItem to show multiple items at once.

1
2
3
4
5
<Carousel  opts={{ align: "start" }}  className="w-full">  <CarouselContent>    {Array.from({ length: 5 }).map((_, index) => (      <CarouselItem key={index} className="md:basis-1/2 lg:basis-1/3">        <Card>          <CardContent className="flex aspect-square items-center justify-center p-6">            <Heading as="span" size="3xl" weight="semibold">              {index + 1}            </Heading>          </CardContent>        </Card>      </CarouselItem>    ))}  </CarouselContent>  <CarouselPrevious />  <CarouselNext /></Carousel>

Loop

Pass opts={{ loop: true }} to wrap from the last slide back to the first.

1
2
3
4
5
<Carousel opts={{ loop: true }} className="w-full">  <CarouselContent>    {Array.from({ length: 5 }).map((_, index) => (      <CarouselItem key={index} className="md:basis-1/2 lg:basis-1/3">        <Card>          <CardContent className="flex aspect-square items-center justify-center p-6">            <Heading as="span" size="3xl" weight="semibold">              {index + 1}            </Heading>          </CardContent>        </Card>      </CarouselItem>    ))}  </CarouselContent>  <CarouselPrevious />  <CarouselNext /></Carousel>

Autoplay

Pair with the embla-carousel-autoplay plugin and forward onMouseEnter / onMouseLeave to pause on hover.

1
2
3
4
5
import Autoplay from "embla-carousel-autoplay";function CarouselAutoplayDemo() {  const plugin = React.useRef(Autoplay({ delay: 2000, stopOnInteraction: true }));  return (    <Carousel      plugins={[plugin.current]}      className="w-full"      onMouseEnter={plugin.current.stop}      onMouseLeave={() => plugin.current.play()}    >      <CarouselContent>        {Array.from({ length: 5 }).map((_, index) => (          <CarouselItem key={index} className="md:basis-1/2 lg:basis-1/3">            <Card>              <CardContent className="flex aspect-square items-center justify-center p-6">                <Heading as="span" size="3xl" weight="semibold">                  {index + 1}                </Heading>              </CardContent>            </Card>          </CarouselItem>        ))}      </CarouselContent>      <CarouselPrevious />      <CarouselNext />    </Carousel>  );}

Vertical

Set orientation="vertical" to slide along the y-axis. Constrain CarouselContent height so the rail is tall enough to scroll.

1
2
3
4
5
<Carousel  orientation="vertical"  opts={{ align: "start" }}  className="w-full">  <CarouselContent className="h-[280px]">    {Array.from({ length: 5 }).map((_, index) => (      <CarouselItem key={index} className="basis-1/2">        <Card>          <CardContent className="flex h-full items-center justify-center p-6">            <Heading as="span" size="2xl" weight="semibold">              {index + 1}            </Heading>          </CardContent>        </Card>      </CarouselItem>    ))}  </CarouselContent>  <CarouselPrevious />  <CarouselNext /></Carousel>

Props

PropTypeDefaultDescription
orientation"horizontal" | "vertical""horizontal"Slide axis. Vertical mode requires a height constraint on CarouselContent.
optsEmblaOptionsTypeForwarded to Embla. See the options reference.
pluginsEmblaPluginType[]Embla plugin instances such as Autoplay, ClassNames, or custom plugins.
setApi(api: CarouselApi) => voidReceives the Embla API once initialized — use to drive carousel state from outside.
classNamestringMerged onto the root region wrapper.

CarouselContent / CarouselItem

Plain div wrappers — accept className for layout adjustments. Use Tailwind basis-* utilities on CarouselItem to control per-slide width.

CarouselPrevious / CarouselNext

Render Reva IconButtons with data-slot="carousel-previous" / data-slot="carousel-next". Inherit IconButton props (variant, size, className); defaults are variant="outline" and size="sm".

On this page