For the complete documentation index, see llms.txt. Markdown variants are available by appending .md to any URL or sending an Accept: text/markdown header. An agent skill is available at /.well-known/agent-skills/site-skill.md.
Home

Carousel

A swipeable slideshow for cycling through content — images, cards, or any elements.

1
2
3
4
5
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "@/components/ui/carousel";

export const CarouselDemo = () => (
    <Carousel className="w-full max-w-xs">
      <CarouselContent>
        {Array.from({ length: 5 }).map((_, index) => (
          <CarouselItem key={index}><div className="flex aspect-square items-center justify-center rounded-lg border bg-card p-6"><span className="text-4xl font-semibold">{index + 1}</span></div></CarouselItem>
        ))}
      </CarouselContent>
      <CarouselPrevious />
      <CarouselNext />
    </Carousel>
  );

Sizes

Control how many items are visible at once using basis-* on CarouselItem.

// 1/3 width — 3 items visible at a time
<Carousel className="w-full max-w-sm">
  <CarouselContent>
    {items.map((item) => (
      <CarouselItem key={item} className="basis-1/3">
        ...
      </CarouselItem>
    ))}
  </CarouselContent>
  <CarouselPrevious />
  <CarouselNext />
</Carousel>

Spacing

Add pl-* to CarouselItem to control gap between slides. The default is pl-4.

<CarouselItem className="pl-2 md:basis-1/2">
  ...
</CarouselItem>

Vertical

Set orientation="vertical" for a top-to-bottom scroll direction.

<Carousel orientation="vertical" className="w-full max-w-xs">
  <CarouselContent className="h-64">
    {items.map((item) => (
      <CarouselItem key={item}>...</CarouselItem>
    ))}
  </CarouselContent>
  <CarouselPrevious />
  <CarouselNext />
</Carousel>

With API

Access the Embla API via setApi to control the carousel programmatically or read state.

"use client";
 
import { useEffect, useState } from "react";
import type { CarouselApi } from "@/components/ui/carousel";
import { Carousel, CarouselContent, CarouselItem } from "@/components/ui/carousel";
 
export function CarouselWithApi() {
  const [api, setApi] = useState<CarouselApi>();
  const [current, setCurrent] = useState(0);
  const [count, setCount] = useState(0);
 
  useEffect(() => {
    if (!api) return;
    setCount(api.scrollSnapList().length);
    setCurrent(api.selectedScrollSnap() + 1);
    api.on("select", () => setCurrent(api.selectedScrollSnap() + 1));
  }, [api]);
 
  return (
    <div>
      <Carousel setApi={setApi}>
        <CarouselContent>
          {Array.from({ length: 5 }).map((_, i) => (
            <CarouselItem key={i}>Slide {i + 1}</CarouselItem>
          ))}
        </CarouselContent>
      </Carousel>
      <p className="text-center text-sm text-muted-foreground mt-2">
        {current} of {count}
      </p>
    </div>
  );
}

Autoplay

Use embla-carousel-autoplay plugin for automatic slide advancement.

pnpm add embla-carousel-autoplay
import Autoplay from "embla-carousel-autoplay";
 
<Carousel plugins={[Autoplay({ delay: 3000 })]}>
  ...
</Carousel>

Props

PropTypeDefaultDescription
orientation"horizontal" | "vertical""horizontal"Scroll direction
optsEmblaOptionsType—Embla carousel options
pluginsEmblaPluginType[]—Embla plugins (e.g. autoplay)
setApi(api: CarouselApi) => void—Callback to access the Embla API
classNamestring—Additional CSS classes

CarouselItem

PropTypeDefaultDescription
classNamestring—Use basis-* to set slide width (e.g. basis-1/2)

CarouselPrevious / CarouselNext

PropTypeDefaultDescription
classNamestring—Override position or size

Usage

import {
  Carousel,
  CarouselContent,
  CarouselItem,
  CarouselNext,
  CarouselPrevious,
} from "@/components/ui/carousel";
 
<Carousel className="w-full max-w-xs">
  <CarouselContent>
    {items.map((item) => (
      <CarouselItem key={item.id}>
        <img src={item.src} alt={item.alt} />
      </CarouselItem>
    ))}
  </CarouselContent>
  <CarouselPrevious />
  <CarouselNext />
</Carousel>

Specs

# Mint — Carousel Specs
 
## Component
- name: carousel
- file: registry/mint/carousel.tsx
- installs to: components/ui/carousel.tsx
- dependencies: embla-carousel-react, embla-carousel, lucide-react
- registryDependencies: button
 
## What is it
A swipeable, keyboard-navigable slideshow built on Embla Carousel. Use it for
images, cards, testimonials, or any repeating sequence of content. Touch drag
and mouse drag are enabled by default; navigation arrows are rendered outside
the slide track as outline icon buttons. Do not use when content must be
simultaneously visible and compared — prefer a grid or table instead.
 
## Visual identity
Navigation buttons inherit the outline variant from Button: transparent fill,
a soft glassy green-tinted border, rounded-full shape. The slide track itself
is unstyled, leaving visual character entirely to the content placed inside
each CarouselItem.
 
## Design tokens
--border: oklch(0.88 0.04 155 / 60%) / oklch(0.32 0.05 155 / 40%)    — nav button border (outline variant)
--input: oklch(0.88 0.04 155 / 60%) / oklch(0.32 0.05 155 / 40%)      — nav button border on hover (outline variant)
--background: oklch(1 0 0) / oklch(0.145 0 0)        — nav button fill (outline variant)
--foreground: oklch(0.145 0 0) / oklch(0.985 0 0)    — nav button icon colour
--accent: oklch(0.92 0.05 155) / oklch(0.26 0.05 155)         — nav button hover background
--accent-foreground: oklch(0.32 0.1 155) / oklch(0.88 0.06 155) — nav button hover icon colour
--ring: oklch(0.5 0.14 155) / oklch(0.62 0.15 155)          — nav button focus ring
 
## Sub-components
- Carousel          → root context provider; sets role="region" aria-roledescription="carousel"; handles keyboard and Embla init
- CarouselContent   → overflow-hidden viewport + flex track; negative margin offsets item padding
- CarouselItem      → individual slide; role="group" aria-roledescription="slide"; basis-full by default
- CarouselPrevious  → prev arrow button; absolute-positioned outside left/top edge; disabled when at start
- CarouselNext      → next arrow button; absolute-positioned outside right/bottom edge; disabled when at end
 
## External dependencies
- embla-carousel-react: core carousel engine (scroll physics, API, plugins)
- embla-carousel: TypeScript types (EmblaCarouselType, EmblaOptionsType)
 
## States
- disabled: nav buttons get opacity-50 pointer-events-none when canScrollPrev / canScrollNext is false
- focus-visible: 3px ring in --ring colour on nav buttons (inherited from buttonVariants outline)
- hover: nav buttons show --accent background tint (inherited from buttonVariants outline)
 
## Conventions
- Wrap Carousel in a container with max-w-* to constrain visible width
- Nav buttons position at -left-12 / -right-12 (horizontal) or -top-12 / -bottom-12 (vertical) — add px-12 to the parent to avoid clipping
- Control slide width via basis-* on CarouselItem (e.g. basis-1/2 for two-up, basis-1/3 for three-up)
- Control inter-slide gap via pl-* on CarouselItem (default pl-4); match with -ml-* on CarouselContent
- For vertical carousels, set a fixed height on CarouselContent (e.g. h-64) to define the viewport
- Access the Embla API programmatically via the setApi prop to build dot indicators or auto-advance logic
- Add plugins via the plugins prop — e.g. Autoplay from embla-carousel-autoplay
- Do not place interactive elements inside CarouselItem that conflict with the swipe gesture (e.g. horizontal sliders)
- Sub-components expose data-slot="carousel", "carousel-content", "carousel-item", "carousel-previous", "carousel-next" for CSS targeting

Installation

GIN Design Plugin
$ npx shadcn add https://gin-design.vercel.app/carousel