Carousel

Accessible React carousel with arrows, dots, keyboard, swipe, and loop, using the carousel role pattern with live announcements. RTL-aware navigation.

Carousel shows one slide at a time inside CarouselViewport. Navigation works with arrows, dots, keyboard, and swipe; add loop to wrap around.

When to use

Use Carousel for a small set of equally important slides, such as featured services or announcements on a portal home page. Keep it to a handful of slides and never autoplay content the user needs to read.

If users must compare items, a grid is better than a carousel.

Examples

Dots

Arrows

Arrows, dots, and loop

Props

Props declared by Carousel. Native attributes of the underlying element pass through.

<Carousel>

PropTypeDefaultDescription
indexnumberControlled active index (a slide, or a page when slideSize is set).
defaultIndexnumberUncontrolled initial index. Defaults to 0.
onIndexChange(index: number) => voidFires with the new index on any navigation.
controls"both" | "dots" | "arrows"Which controls to lay out: dots below, arrows flanking, or both.
loopbooleanWrap past the ends instead of stopping.
dotSizeDotSizeDot indicator diameter: sm 8 · md 12 · lg 16 px.
arrowStyleArrowStyleArrow button style: filled green primary, transparent subtle, or the square gray neutral (SDGA Button Neutral).
arrowSizeArrowSizeArrow button diameter: sm 32 · md 40 · lg 48 px.
swipebooleanEnable pointer/touch swipe to navigate. Defaults to true.
autoplaybooleanAuto-advance (house extension; pauses on hover/focus + reduced-motion).
autoplayIntervalnumberAuto-advance interval in ms. Defaults to 5000.
slideSizestringFixed inline size of every slide (any CSS length). Switches to page-based navigation: the viewport shows as many whole slides as fit, and arrows/dots move one page at a time. Without it, each slide fills the viewport.
gapstringSpace between slides (any CSS length). Defaults to 0.
align"center" | "start"Where the active page sits when slides don't fill the viewport; center lets the neighbours peek at both edges.
flushbooleanDrop the SDGA shell padding (inline 24 / block 32) so the carousel sits flush in its container.
arrowGapstringSpace between the arrows and the viewport (flank, SDGA 44px) or their inset from its edges (overlay, 0).
arrowPlacement"overlay" | "flank"flank (default) lays the arrows out beside the viewport; overlay floats them over its edges.
announce(index: number, count: number) => stringBuilds the screen-reader announcement after navigation (a visually hidden status). Defaults to Slide {n} of {m}.

<CarouselSlide>

PropTypeDefaultDescription
indexnumber0-based position - injected by CarouselViewport.

<CarouselNav>

PropTypeDefaultDescription
labelstringAccessible label for the icon-only button.
asChildbooleanCompose with a custom element (e.g. a router link) via Radix Slot.
childrenreact.ReactNodeOverride the default chevron glyph.

<CarouselDots>

PropTypeDefaultDescription
barebooleanDrop the pill padding (SDGA "Dots Only").
dotLabel(index: number, count: number) => stringBuild each dot's accessible label. Defaults to Go to slide {n}.

Accessibility

The carousel renders role="region" with aria-roledescription="carousel", and each slide is a role="group" with a slide description. The left and right arrow keys move between slides, and Home and End jump to the ends. Inactive slides are marked inert, so hidden content is not focusable.

Slide changes are announced politely, previous and next have accessible names, and the current dot carries aria-current.