Carousel
ProA slide viewer with a clickable indicator. The track slides or fades between full-width or peeking slides, arrow keys step it, autoplay counts down in the indicator, and the indicator ships with a closed set of forms: dots, lines, fraction, thumbnails.
Installation#
Usage#
The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.
Have a license? Unlock it here
Anatomy#
import {
Carousel,
CarouselContent,
CarouselSlide,
CarouselCaption,
CarouselPrevious,
CarouselNext,
CarouselIndicators,
CarouselPlayPause,
} from "@/components/ui/carousel"
<Carousel>
<CarouselContent>
<CarouselSlide>
<CarouselCaption />
</CarouselSlide>
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
<CarouselIndicators />
<CarouselPlayPause />
</Carousel>Indicator variants#
The indicator ships as one component with a closed set of forms, picked with the variant prop. Never hand-roll a new indicator per carousel: pick the form that fits the surface.
dots(default): pill-morphing bullets, for most cards and testimonials.lines: thin fixed-width ticks, a quieter alternative to dots.fraction: a contained 2 / 5 readout for dense image galleries; the active number rolls like an odometer, and the pill grows smoothly by one digit-width when the count crosses a boundary (99 / 100).thumbnails: one slide preview per slide; pass athumbnailsnode array indexed to the slides. A single ink ring glides from tile to tile rather than toggling per preview.tabs: one text label per slide; pass alabelsarray indexed to the slides. A single underline glides to the active tab rather than toggling per label.numbers: a numbered chip per slide; the active one fills like a numbered pager. Good for short, countable sets.progress: a single continuous bar that fills with position (25% on slide 1 of 4). A readout, not per-slide clickable; navigate with arrows, drag, or the arrow keys.
Contained#
Pass contained to seat dots, lines or progress in a pill, the same one fraction wears. In the flow it is a muted fill; with overlay it turns dark and frosted so the white dots hold on any photo. size="lg" scales the dots, lines or bar up for a wide showcase row; contained, its pill stands as tall as an xl button.
Over a hero#
Positioning is orthogonal to the form: any variant composes with overlay (float over the image, white treatment) and align (end bottom-right or center bottom-centered). Full-bleed heroes usually want centered overlay dots.
On images / cards#
For photo galleries, add CarouselPrevious / CarouselNext: overlay arrows that reveal on hover (and on keyboard focus), and stay hidden at the first/last slide. Pass overlay to CarouselIndicators to float the dots over the bottom-right of the image; over photos they switch to fixed white so they stay legible on any background.
Product gallery#
thumbnailSize sets the thumbnail tile: sm (the default, 56×40) suits landscape photos, lg is a 64px square for product packshots, where a landscape crop would cut the object in half. The gliding ring resizes with the tiles. This is the gallery on the ecommerce product pages.
Fade#
effect="fade" on CarouselContent stacks the slides and fades the incoming one in on top of the outgoing one, which holds underneath until the fade is done, so the page never shows through mid-way. Arrows, keys, dots and a swipe all still step it. The first slide is simply there on load; only changes fade.
Autoplay#
autoplay advances on its own: true every five seconds, or a number of milliseconds. The active dot or line counts down the interval. It holds while the pointer is over the carousel, while keyboard focus is inside it and while the tab is hidden, and picks up where it left off; a manual step starts a fresh interval. Autoplay turns loop on, so the arrows and keys wrap too. CarouselPlayPause is the switch that lasts, for anyone who needs the slides to hold still.
Peek#
peek on CarouselContent narrows the slides to centered cards with the neighbours showing at both edges. With loop or autoplay the row never ends: the last card steps forward onto the first instead of rewinding past them all. CarouselCaption sets words over a card's photo, and the neighbours' captions dim so only the middle one reads at full strength. Size the cards with [--carousel-slide:80%] and the gutter with [--carousel-gap:2rem] in its className.
Slideshow#
kenBurns adds a slow push-in and drift to each slide's photo (its direct img, video or picture child) while it shows, alternating direction so consecutive photos never travel the same way. With a fade and autoplay it is a slideshow: a welcome or loading screen, a hero backdrop. Put the words outside the slides and they hold still while the photos move. Off under reduced motion.
Controlled#
Pass index and onIndexChange to drive the active slide from your own state, e.g. to pair the dots with external prev/next buttons or a step counter. Omit them for uncontrolled use with defaultIndex.
API reference#
Carousel
The root, a role="group" region with aria-roledescription="carousel"; ArrowLeft and ArrowRight step it while focused. Forwards all native <div> props. Every part accepts className, merged last.
| Prop | Type | Default | Description |
|---|---|---|---|
| index | number | - | The active slide, 0-based, controlled. |
| defaultIndex | number | 0 | The first slide shown, uncontrolled. |
| onIndexChange | (index: number) => void | - | Called with the next index on navigation. |
| label | string | "Carousel" | The accessible name of the region. |
| autoplay | boolean | number | false | Advances every 5s (true) or every n ms. Holds on hover, focus, off-screen, in a hidden tab and when paused. Turns loop on. |
| loop | boolean | autoplay | Wraps past either end for arrows, keys and swipe. On by default only with autoplay. |
| Data attribute | Description |
|---|---|
| data-autoplay | "playing" or "paused" while autoplay is set. |
| CSS variable | Description |
|---|---|
| --carousel-interval | The autoplay interval in ms, written from autoplay. Times the Ken Burns drift and the progress fill. |
CarouselContent
The clipping viewport and its moving track. Children must be CarouselSlides. Forwards all native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| draggable | boolean | true | Mouse and touch drag-to-swipe. A real drag swallows the trailing click. |
| effect | "slide" | "fade" | "slide" | Translates the track, or crossfades stacked slides. |
| kenBurns | boolean | false | A slow push-in on each slide's direct img, video or picture. Off under reduced motion. |
| peek | boolean | false | Centered card slides with the neighbours showing, endless with loop. effect="slide" only. |
| CSS variable | Description |
|---|---|
| --carousel-slide | With peek, the width of a card (87.5% by default). Set it in className. |
| --carousel-gap | With peek, the gutter between cards. |
CarouselSlide
One slide, labelled with its position ("2 of 4"). Forwards all native <div> props.
| Data attribute | Description |
|---|---|
| data-active | "true" on the slide showing. |
CarouselCaption
A title, line and action over the foot of the photo on a legibility scrim; put it after the photo. Dims on non-centered peek cards. Forwards all native <div> props.
CarouselPrevious
An overlay arrow that reveals on hover and focus, disabled at the first slide unless looping. A Button (every prop except variant, size and iconOnly).
CarouselNext
The forward twin of CarouselPrevious, disabled at the last slide unless looping.
CarouselIndicators
Position and navigation in one of seven forms; renders nothing for a single slide. Forwards all native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "dots" | "lines" | "fraction" | "thumbnails" | "tabs" | "numbers" | "progress" | "dots" | The form. fraction and progress are readouts, not per-slide buttons. |
| overlay | boolean | false | Floats the indicator over the image in a fixed white treatment. |
| align | "end" | "center" | "end" | Where an overlay indicator sits: bottom-right or bottom-centered. |
| contained | boolean | false | Seats dots, lines or progress in a pill (muted, or frosted dark over media). |
| size | "md" | "lg" | "md" | The scale of dots, lines and progress. |
| thumbnails | React.ReactNode[] | - | One preview per slide, in slide order. Required by thumbnails. |
| thumbnailSize | "sm" | "lg" | "sm" | A 56 by 40 landscape tile, or a 64px square for packshots. |
| labels | React.ReactNode[] | - | One short label per slide for tabs, falling back to the slide number. |
| dotLabel | (index: number) => string | i => Go to slide ${i + 1} | Builds the accessible label of each per-slide button. |
| Data attribute | Description |
|---|---|
| data-active | Present on the item of the slide showing (it also gets aria-current). |
CarouselPlayPause
Pauses and resumes autoplay for good (WCAG 2.2.2); renders nothing without autoplay. A Button (every prop except variant, iconOnly and children); place it yourself.
| Prop | Type | Default | Description |
|---|---|---|---|
| overlay | boolean | false | The frosted dark chip for a button on the photo. |
| size | "sm" | "md" | "lg" | "xl" | "sm" | The Button size. |