Skip to content
Tier

Carousel

Pro

The slides, one at a time or a few, that a person moves through with buttons, arrow keys or by swiping. It never moves by itself unless you set an interval. Under reduced motion or data-motion="calm" the slides jump instead of gliding and the interval stops; data-motion="none" does the same.

Markdown
Version 5.2.0· other versions

This page is for 5.2.0, the current release. An earlier release’s page is the Markdown that release published, so the docs match the version you have installed (candor-lock.json records it).

Not in the docs of 1.3.0, 1.4.0, 1.5.0, 1.5.1, 1.6.0, 1.7.0, 1.8.0, 1.8.1, 1.9.0, 1.9.1, 2.0.0, 2.1.0 and 2.2.0: the page came later, or had another name then.

Docs pages weren’t kept for 1.0.0, 1.0.1, 1.1.0, 1.1.1, 1.2.0 and 1.2.1: they are kept from 1.3.0 on, and before that only llms.txt was published.

Preview

Loading the preview

Install

With the Candor CLI

First time? Get started sets up the registry and theme. Or with shadcn directly: npx shadcn@latest add @candor/carousel

Brings in: button, locale, motion, tokens, use-announcer, utils · npm: lucide-react@^1.67.0

Usage

TSX · The example above
"use client"

import * as React from "react"
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, CarouselStatus, type CarouselTransition } from "@/components/candor/ui/carousel"

const reports = [
  { title: "Weekly deploys", body: "31 deploys, 2 rolled back." },
  { title: "Open incidents", body: "1 open, 4 resolved this week." },
  { title: "Build time", body: "Median 6 min 12 s over the last 100 builds." },
  { title: "Test flakes", body: "3 tests failed and passed on retry." },
]

const transitions: CarouselTransition[] = ["slide", "fade", "vertical", "blur", "zoom", "uncover", "fill"]

// It never moves by itself: add `interval` to auto-advance, and it still stops for the pointer, focus and reduced motion.
// `transition` other than "slide" stacks the slides and draws each change (the drawings are the Pro motion-drawings item's).
export default function Example() {
  const [transition, setTransition] = React.useState<CarouselTransition>("slide")
  return (
    <div className="flex max-w-md flex-col gap-3">
      <label className="flex items-center gap-2 text-xs text-muted-foreground">
        transition
        <select value={transition} onChange={(e) => setTransition(e.target.value as CarouselTransition)} className="rounded-md border bg-background px-2 py-1 text-xs text-foreground">
          {transitions.map((t) => (
            <option key={t}>{t}</option>
          ))}
        </select>
      </label>
      <Carousel key={transition} label="Weekly reports" transition={transition}>
        <CarouselContent>
          {reports.map((r) => (
            <CarouselItem key={r.title}>
              <div className="flex h-32 flex-col justify-center gap-1 rounded-md border bg-card px-16">
                <h3 className="text-sm font-medium">{r.title}</h3>
                <p className="text-sm text-muted-foreground">{r.body}</p>
              </div>
            </CarouselItem>
          ))}
        </CarouselContent>
        <CarouselPrevious />
        <CarouselNext />
        <div className="mt-2 text-center">
          <CarouselStatus />
        </div>
      </Carousel>
    </div>
  )
}
PropTypeDefaultDescription
intervalnumber | undefined—Milliseconds between automatic advances. Left out, the carousel never moves by itself. When set it still stays still while the pointer is over it or focus is inside it, after the person pauses it, and under reduced motion, `data-motion="calm"` or `data-motion="none"`, and it shows a pause button.
transitionCarouselTransition | undefined"slide"How a change of slide is drawn. `slide` (the default) is a scrolling row the slides glide along. The others stack the slides in one place, show one at a time and draw the change: `fade`, `vertical` (rising from below), `blur`, `zoom`, `uncover` (uncovered from the end of the line) or `fill` (uncovered in four steps). The slides not showing are `inert` and hidden from screen readers, and a change the person makes is announced ("2 of 5"). A finger or a pen swipes; a mouse drag selects text. The drawings come with the Pro motion-drawings item; without it a stacked carousel changes slide at once.
onIndexChange((index: number) => void) | undefined—Called with the first visible slide's position (from 0) when it changes, by the buttons, keys, swiping or the interval.
labelstring | undefined—The carousel's accessible name. Defaults to "Carousel".
classNamestring | undefined—Extra classes for the outer element.
childrenReact.ReactNode | undefined—`CarouselContent` with the slides, and the previous, next and status controls.

<CarouselContent>

The scrolling row of slides; its direct children are the CarouselItems. It scrolls and snaps natively, so a finger or trackpad moves it exactly as far as the pointer goes. It is a tab stop: Left and Right (mirrored in right-to-left languages) change slide, Home and End go to the ends, while it has focus itself: in a field, a link or a button inside a slide, the keys are that control's.

No props.

Also takes the props of React.ComponentProps<"div">.

<CarouselItem>

One slide, labelled "3 of 8" for assistive technology. Give it a width with basis-* to show more than one at a time.

No props.

Also takes the props of React.ComponentProps<"div">.

<CarouselPrevious>

Goes to the previous slide. At the first slide it stays focusable, is aria-disabled and says so.

PropTypeDefaultDescription
classNamestring | undefined—Extra classes for the outer element.

<CarouselNext>

Goes to the next slide. At the last slide it stays focusable, is aria-disabled and says so.

PropTypeDefaultDescription
classNamestring | undefined—Extra classes for the outer element.

<CarouselStatus>

"3 of 8": the first visible slide out of all of them, in the locale's digits. Announced politely when it changes (in a stacked carousel, by the carousel itself, so it is said once).

PropTypeDefaultDescription
classNamestring | undefined—Extra classes for the outer element.

Strings

The fixed words, in English by default. Change any of them under carousel in <LocaleProvider strings={{ … }}>; the table is exported as carouselStrings.

KeyEnglish
carouselCarousel
slideSlide
slideOf{current} of {total}
previousPrevious slide
nextNext slide
atStartThis is the first slide.
atEndThis is the last slide.
pausePause automatic advance
resumeResume automatic advance

Motion

MomentFamilyWhat it does
changearrive, riseIn a stacked carousel, the slide the person moved to comes in as its transition draws it (a fade, a rise, a blur, a zoom, or uncovered). Only on a change, never at first draw; a short fade under reduced motion, and at once at the Off level or without the Pro drawings.
slidemoveThe slides glide to the one the person chose with the buttons or arrow keys, or to the next one when the interval fires. Swiping moves the row exactly as far as the finger goes, with nothing added. Under reduced motion the slides jump.

Set with --cd-motion-carousel-<moment>-* in your brand file; the families are in the design reference.

Types

CarouselTransition
How a change of slide is drawn: slides side by side in a scrolling row, or stacked in one place with a drawing.
"slide" | "fade" | "vertical" | "blur" | "zoom" | "uncover" | "fill"

Terms used here

Calm level
What Candor's motion does for a person whose operating system asks for reduced motion: the highest-precedence setting, above the app's own motion level (none, calm, subtle or expressive) and the brand file. A theme tunes how a change looks, never when it happens, so nothing depends on seeing the motion.

Every term is defined in the glossary (Markdown).