kosori

Carousel

A slide track with previous and next controls, built on Embla Carousel.

Carousel wraps embla-carousel-react in a track, a slide and two icon controls. Each slide takes the full width of the track, and the controls scroll the track one slide at a time. Arrow keys work when the track has focus.

It is an Authored component: shadcn/ui owns its structure and its design comes from the nearest Port, because coss/ui ships no carousel. The controls inherit their surface, focus ring and disabled treatment from Button, and a slide inherits its sizing from AspectRatio.

Installation

npx shadcn@latest add @kosori/carousel

Example

carousel-example.tsx
OneThe first slide of the default carousel.
TwoSlides scroll with the previous and next controls.
ThreeThe track also responds to the arrow keys.
FourEvery slide shares the same width.
FiveThe last slide disables the next control.
carousel-example
import { AspectRatio } from '@/components/ui/aspect-ratio';
import {
  Carousel,
  CarouselContent,
  CarouselItem,
  CarouselNext,
  CarouselPrevious,
} from '@/components/ui/carousel';

const SLIDES = [
  {
    className:
      'overflow-hidden rounded-xl border bg-linear-to-br from-primary/16 to-background',
    description: 'The first slide of the default carousel.',
    title: 'One',
  },
  {
    className:
      'overflow-hidden rounded-xl border bg-linear-to-br from-info/16 to-background',
    description: 'Slides scroll with the previous and next controls.',
    title: 'Two',
  },
  {
    className:
      'overflow-hidden rounded-xl border bg-linear-to-br from-success/16 to-background',
    description: 'The track also responds to the arrow keys.',
    title: 'Three',
  },
  {
    className:
      'overflow-hidden rounded-xl border bg-linear-to-br from-warning/16 to-background',
    description: 'Every slide shares the same width.',
    title: 'Four',
  },
  {
    className:
      'overflow-hidden rounded-xl border bg-linear-to-br from-destructive/16 to-background',
    description: 'The last slide disables the next control.',
    title: 'Five',
  },
];

const CarouselExample = () => (
  <div className='w-full max-w-md px-12'>
    <Carousel>
      <CarouselContent>
        {SLIDES.map((slide) => (
          <CarouselItem key={slide.title}>
            <AspectRatio className={slide.className} ratio={16 / 9}>
              <div className='flex size-full flex-col justify-end gap-1 p-5'>
                <span className='font-medium text-lg'>{slide.title}</span>
                <span className='text-muted-foreground text-sm'>
                  {slide.description}
                </span>
              </div>
            </AspectRatio>
          </CarouselItem>
        ))}
      </CarouselContent>
      <CarouselPrevious />
      <CarouselNext />
    </Carousel>
  </div>
);

export default CarouselExample;

Usage

import {
  Carousel,
  CarouselContent,
  CarouselItem,
  CarouselNext,
  CarouselPrevious,
} from '@/components/ui/carousel';

const CarouselDemo = () => (
  <Carousel className='w-full max-w-sm'>
    <CarouselContent>
      <CarouselItem>Slide one</CarouselItem>
      <CarouselItem>Slide two</CarouselItem>
    </CarouselContent>
    <CarouselPrevious />
    <CarouselNext />
  </Carousel>
);

The previous and next controls sit outside the track, so leave room for them: the Example pads its container by px-12.

Orientation

orientation switches the scroll axis. The controls rotate and move above and below the track, so a vertical carousel needs vertical room instead:

<Carousel className='h-64 w-full max-w-xs' orientation='vertical'>
  <CarouselContent>
    <CarouselItem>Slide one</CarouselItem>
    <CarouselItem>Slide two</CarouselItem>
  </CarouselContent>
  <CarouselPrevious />
  <CarouselNext />
</Carousel>

API

PropTypeDefaultPurpose
optsEmbla optionsundefinedForwarded to Embla Carousel
orientationhorizontal, verticalhorizontalScroll axis
pluginsEmbla pluginsundefinedForwarded to Embla Carousel
setApi(api: CarouselApi) => voidundefinedReceives the Embla API

useCarousel reads the nearest carousel's API and scroll state, and throws outside a Carousel.

On this page