Carousel

Embla-powered carousel with aspect-ratio presets and responsive navigation placement.

Installation

npx shadcn@latest add @iconiq/carousel

File Structure

Usage

"use client";

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

const slides = [
  "Ship interfaces that feel polished from the first slide.",
  "Motion, spacing, and type that stay in sync.",
  "Drop in components—skip the layout guesswork.",
  "Scroll stories without losing your rhythm.",
  "Build faster. Keep the craft.",
] as const;

export function CarouselUsage() {
  return (
    <Carousel className="w-full max-w-md sm:max-w-lg">
      <CarouselContent>
        {slides.map((slide) => (
          <CarouselItem key={slide}>
            <div className="flex h-full items-center justify-center p-1">
              <p className="px-6 text-center font-light text-lg leading-snug text-balance text-muted-foreground">
                {slide}
              </p>
            </div>
          </CarouselItem>
        ))}
      </CarouselContent>
      <CarouselPrevious />
      <CarouselNext />
    </Carousel>
  );
}

Props

Props
Description

Carousel

orientation

Sets the Embla scroll axis and the spacing applied between slides.

Type "horizontal" | "vertical"·Default horizontal

aspectRatio

Sets the slide viewport shape on `CarouselContent`. Use presets like `video` (16:9) and `square`, or pass a custom ratio string such as `21/9`.

Type "square" | "video" | "4/3" | "3/2" | "portrait" | string·Default video

navPlacement

Positions previous and next controls. `responsive` places controls below the carousel on the right on mobile and outside the track from `sm` upward.

Type "outside" | "responsive"·Default responsive

autoplay

When enabled, advances slides on a timer. Pass a number for the delay in milliseconds. Pair with `opts={{ loop: true }}` for continuous playback.

Type boolean | number

opts

Forwarded Embla options such as `align`, `loop`, or `slidesToScroll`. The root merges `axis` from `orientation`.

Type CarouselOptions

plugins

Optional Embla plugins passed to `useEmblaCarousel`, such as `embla-carousel-autoplay`.

Type CarouselPlugin

setApi

Optional callback that receives the Embla API instance after mount for external index or dot indicators.

Type (api: CarouselApi) => void

aria-label

Accessible label for the root `role="region"` carousel wrapper.

Type string·Default Carousel

className

Optional class names merged onto the root `role="region"` wrapper.

Type string

CarouselContent

className

Optional class names merged onto the inner flex container that holds slides.

Type string

CarouselItem

className

Optional class names merged onto each slide group.

Type string

CarouselPrevious / CarouselNext

className

Optional class names merged onto the control for local offsets or sizing.

Type string

useCarousel

selectedIndex

Zero-based index of the active scroll snap.

Type number

scrollSnapCount

Total number of scroll snaps reported by Embla.

Type number

scrollTo

Scrolls directly to the requested snap index.

Type (index: number) => void

scrollPrev / scrollNext

Moves to the previous or next snap.

Type () => void

api

Underlying Embla API instance when you need lower-level control.

Type CarouselApi

Registry bundle

Install the exact registry entry shown on the right when you want the component file and its declared runtime dependencies together.

Dependencies: embla-carousel-react, lucide-react.

Navigation controls are built into the carousel file — no separate button install is required.

Built-in `autoplay` pauses after drag or manual navigation, then resumes after the configured delay. Pair with `opts={{ loop: true }}` for continuous playback.

Custom aspect ratios such as `21/9` are applied with inline `aspect-ratio` styles so Tailwind JIT does not need a safelist entry.

Default `navPlacement` is `responsive`: controls sit below the carousel on the right on mobile and outside from `sm` upward.

The generated registry file is /r/carousel.json.

Contact

Additionally, if you find any bug or issue, feel free to raise an issue.

Ship interfaces that feel polished from the first slide.