Skip to content

Carousel

A horizontally or vertically scrolling set of slides with prev/next controls.

Stablev0.4.2added in v0.1.0@garn/ui/carousel
On this page

Default

Five numbered slides with prev/next.

Installation

garn is copy-in — the garn CLI writes this component's source into your project, so you own and can edit it. It resolves any base files and installs the npm dependencies below for you.

terminal
npx garn-ui add carousel
First time? Set up garn in your project
Registry
carousel
Deps
embla-carousel-reactlucide-react
Registry deps
buttonutils

Import

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

Anatomy

The parts this component is built from — compose them to assemble it.

CarouselRequired[data-slot="carousel"]

Root (role=region, aria-roledescription=carousel) — sets orientation/opts/plugins, exposes the Embla api via setApi, and handles arrow-key nav.

CarouselContentRequired[data-slot="carousel-content"]

The Embla viewport + flex track that holds the items.

CarouselItemRequired[data-slot="carousel-item"]

One slide (role=group, aria-roledescription=slide); full-basis by default.

CarouselPrevious[data-slot="carousel-previous"]

Previous-slide Button; auto-disables at the start.

CarouselNext[data-slot="carousel-next"]

Next-slide Button; auto-disables at the end.

Props

The component's public props and their types.

opts
Description
Embla options (loop, align, dragFree, …) passed straight to the engine.
Type
Partial<OptionsType>
orientation
Description
Scroll axis; sets the Embla `axis` and the track's flex direction.
Type
"horizontal" | "vertical"
Default
"horizontal"
plugins
Description
Embla plugins (autoplay, wheel gestures, …).
Type
CreatePluginType<LoosePluginType, {}>[]
setApi
Description
Receives the Embla `api` once ready — for programmatic control or a dot indicator.
Type
(api: CarouselApi) => void

Plus 280 inherited native HTML attributes.

Styling

Target these data-slots and states, and remap these tokens, to restyle without forking the component.

[data-slot="carousel"]

States

SelectorState
cannot-scroll-prev
cannot-scroll-next

When to use

Show a set of peer items (images, cards, testimonials) in a swipeable/steppable track when they don't all fit at once.

Reach for something else when

  • Essential content users must not miss (don't hide it off-screen)
  • a long ordered list (use a list + pagination)
  • switching between distinct views (use tabs).

Overview

Carousel shows a set of peer items — images, cards, testimonials — in a swipeable/steppable track when they don't all fit at once. Carousel is the Embla-backed region (sets orientation/options, handles arrow keys), CarouselContent the track, CarouselItem each slide, with CarouselPrevious/CarouselNext auto-disabling at the ends. Use it for browsable peer media; for content users must not miss, lay it out directly — carousels hide everything but the current slide.

Guidelines

Use a carousel only for genuinely optional, peer content. Anything important shouldn't depend on a user swiping to find it — carousels have low engagement past the first slide. A short, equally-weighted set (a gallery, featured cards) is the sweet spot.

Make navigation obvious and keyboard-operable. Show prev/next controls and position indicators; the track responds to arrow keys and the buttons disable at the ends — don't hide the only way to advance behind a hover. Let users control the pace.

Avoid aggressive autoplay. If it auto-advances, make it slow, pausable, and stopped on focus/hover; honor reduced-motion. Each slide is announced as one of N — keep slides self-contained so they make sense out of sequence.

Best practices

Do
  • Give the Carousel an aria-label; keep prev/next reachable.
  • If you autoplay, provide a pause/stop control (and respect reduced motion).
  • Grab the Embla api via setApi when you need programmatic control or a dot indicator.
Don't
  • Don't bury important content in later slides.
  • Don't autoplay without a way to pause.
  • Don't use a carousel where the items should all be visible (use a grid).

Content guidelines

  • Give every slide a concise caption or alt text — don't rely on position alone.
  • Label the previous/next controls and any slide indicators for assistive tech.

Troubleshooting

useCarousel throws 'must be used within a <Carousel />'.

Cause. CarouselContent/Item/Previous/Next read context from the Carousel root.

Fix. Wrap all carousel parts in a single <Carousel>…</Carousel>.

Prev/Next don't appear or get clipped.

Cause. They're absolutely positioned just outside the track; a tight/overflow-hidden container hides them.

Fix. Leave horizontal room around the Carousel (or reposition the controls).

Accessibility

Role
region

Keyboard

ArrowLeftPrevious slide (when the carousel has focus).
ArrowRightNext slide (when the carousel has focus).
A1.3.1Info and RelationshipsA2.1.1KeyboardA2.2.2Pause, Stop, Hide
  • Root is role=region aria-roledescription="carousel"; each item is role=group aria-roledescription="slide".
  • Give the carousel an accessible name (aria-label) describing what it contains.
  • Prev/Next have sr-only labels and disable at the ends so state is conveyed without color.