Carousel
Browse a collection of items with carousel navigation.
Carousel displays a draggable collection with optional navigation and pagination. CarouselController owns the items and selection; Carousel controls their presentation.
Import
import { Carousel, CarouselController } from "@puzzle-ui/ui";Basic usage
Supply items to the controller and renderItem to the carousel. Navigation buttons are opt-in: pass backButton and nextButton to support click and keyboard navigation as well as dragging.
Provide getItemKey when items can be inserted, removed, or reordered. Stable keys keep component state attached to its item; without it, keys use the item index.
Responsive panels and gap
panelsPerView controls how many items fit at once. Responsive values use mobile, tablet, and desktop, inheriting smaller breakpoints when omitted. Change the preview device to compare layouts.
Changing gap recalculates panel offsets and pagination, including when panel
widths remain fixed. The active panel stays aligned to its updated position.
Item width and single-page alignment
Use itemWidth instead of panelsPerView for explicit item widths; it takes precedence when both are supplied. singlePageAlignment positions items when the whole collection fits on one page.
Navigation and pagination placement
buttonAlignment="over" overlays navigation on the viewport; "aside" reserves space beside it. pagerAlignment supports "under" and "over". Both accept responsive values. renderPager receives a zero-based page index and the page count.
Controlled selection
currentPanel is the zero-based first visible item index, not a page number. Pair it with onPanelChange to keep application state synchronized. defaultPanel supplies an initial index for uncontrolled usage.
API
ResponsiveValue<T> accepts a value or an object with mobile, tablet, and desktop values.
CarouselController
| Prop | Type | Default | Description |
|---|---|---|---|
items | T[] | Required | Collection shared with descendant carousels. |
children | ReactNode | Required | Carousel and optional custom controls. |
currentPanel | number | - | Controlled first visible item index. |
defaultPanel | number | 0 | Initial uncontrolled first visible item index. |
onPanelChange | (panel: number) => void | - | Called with the next first visible item index. |
Carousel
| Prop | Type | Default | Description |
|---|---|---|---|
renderItem | (item: T, index: number) => ReactNode | Required | Renders each item. |
getItemKey | (item: T, index: number) => Key | Item index | Stable React key for each item. |
panelsPerView | ResponsiveValue<number> | 1 | Number of visible items in panel sizing mode. |
itemWidth | ResponsiveValue<CSSProperties["width"]> | - | Explicit width; takes precedence over panel count. |
gap | ResponsiveValue<CSSProperties["gap"]> | 0 | Space between items. Numbers are pixels. |
singlePageAlignment | "left" | "center" | "right" | "left" | Alignment when all items fit on one page. |
backButton | ReactElement | - | Previous-page control; receives disabled state and click handling. |
nextButton | ReactElement | - | Next-page control; receives disabled state and click handling. |
buttonAlignment | ResponsiveValue<"aside" | "over"> | "over" | Navigation placement. |
pagerAlignment | ResponsiveValue<"under" | "over"> | "under" | Pager placement. |
renderPager | (currentPage: number, numberOfPages: number) => ReactNode | - | Custom pager; page index is zero-based. |
ariaLabel | string | "Carousel" | Accessible name of the carousel group. |
className | string | - | Additional class on the root. |
viewportClassName | string | - | Additional class on the clipping viewport. |
viewportStyle | CSSProperties | - | Styles on the viewport. |
For custom controls within the controller, useCarouselContext<T>() exposes items, currentPanel, and setPanel(index).
Accessibility
Give each carousel a descriptive ariaLabel. Navigation elements must be real buttons with accessible names and forward their received props. They are disabled at the collection boundaries. Provide buttons rather than relying on pointer dragging alone.