Skip to content
Component

Carousel

One carousel with three presets: basic autoplay, arrow navigation, and multi-slide layouts. Transform-based slides, progress dots, seamless looping, swipe, and keyboard support, with no external dependency.

Basic autoplay

Defaults to autoplay every 3000ms with no arrows. The active dot fills to show progress until the next slide.

  • Autoplay pauses on hover, focus, touch, and when the tab is hidden, and never runs under prefers-reduced-motion

Configurable interval

Set autoplayInterval; the progress dot animation matches it.

Arrow controls

Pass controls for prev/next arrows overlaid on the edges. Arrows are semantic icon buttons.

Non-looping edges

With loop set to false, the arrows disable themselves at the first and last slide.

Responsive multi-image

slidesPerView takes a responsive map. Slide count changes at CSS breakpoints, not via JavaScript.

Infinite autoplay, multiple cards

Use continuous mode for a smooth, always-moving image track with responsive card widths. It pauses while hovered or focused and has no arrows or bottom indicators.

Controlled index

Drive the active slide from your own state with index and onIndexChange.

  • The track is a labeled region; ArrowLeft and ArrowRight move between slides when it has focus.
  • Each slide is labeled "Slide N of total", and clone slides used for looping are hidden from assistive tech.
  • Autoplay and slide transitions are disabled under prefers-reduced-motion.
  • An imperative handle exposes next, previous, and goTo through a ref.

When to use

  • A small set of visually equivalent items where showing one (or a few) at a time saves space - featured content, a product gallery, a testimonial rotation.
  • Content the user doesn't need all of at once, and can miss slides of without losing anything essential.

When not to use

  • Content that's actually important for every visitor to see - carousels have well-documented low interaction rates; don't hide primary CTAs or key information behind autoplay.
  • Long lists better served by a Grid or scrollable list where everything is visible/scannable at once.
  • A step-by-step flow - use Stepper or Tabs, which better communicate sequence and progress.

Keyboard

KeyAction
ArrowLeft / ArrowRightMoves to the previous/next slide when the track has focus.
TabReaches the prev/next controls and indicator dots when controls/indicators are enabled.

Mobile behavior

  • swipe (default true) enables touch/pointer dragging between slides - this is the primary interaction on touch devices, not a bonus on top of arrow controls.
  • controls (prev/next arrow buttons) default to off - enable them explicitly if touch swipe alone isn't enough for your audience.

Accessibility notes

  • Autoplay automatically pauses on hover, focus, touch, and when the browser tab is hidden - and never runs at all under prefers-reduced-motion.
  • Each slide announces as "Slide N of total" to screen readers; clone slides used to fake seamless looping are hidden from assistive tech (aria-hidden) so they never get double-announced.
  • The track itself is a labeled region so ArrowLeft/ArrowRight navigation is discoverable once focused.

Common mistakes

  • Putting the only copy of important information in slide 2+ of an autoplaying carousel - most users never see past the first slide.
  • Leaving autoplay on for content-heavy slides (long text, forms) - autoplay is for glanceable content, not anything requiring reading time.
  • Disabling swipe on mobile without providing visible arrow controls instead - that leaves touch users with no way to advance.

Known limitations

  • No built-in lazy-loading of off-screen slide images - pair with the Image component's own loading behavior and set `loading="eager"` only for the first visible slide(s).

Related components

Carousel props

index / onIndexChangeNone
number / (index: number) => void

Controlled active slide.

defaultIndex0
number

Uncontrolled initial slide.

slidesPerView1
number | ResponsiveCount

Slides visible at once - a number or a responsive breakpoint map.

autoplaytrue
boolean

Advances automatically; pauses on hover/focus/touch/hidden tab, off entirely under reduced motion.

autoplayInterval3000
number

Milliseconds between autoplay advances.

looptrue
boolean

Wraps seamlessly past the first/last slide.

swipetrue
boolean

Enables touch/pointer drag between slides.

controlsfalse
boolean

Shows overlay prev/next arrow buttons.

indicatorstrue
boolean

Shows bottom dot indicators.

CriterionStatusNote
Keyboard supportSupportedArrowLeft/ArrowRight move slides when the track has focus; controls and dots are separately reachable.
Focus managementNot applicableNot an overlay - no focus trap or portal involved.
Screen reader labelsSupportedEach slide announces as "Slide N of total"; loop clones are aria-hidden.
Reduced motionSupportedAutoplay and slide transitions are fully disabled under prefers-reduced-motion.
Reduced transparencyNot applicableNo translucent surfaces in the base carousel chrome.
Mobile touch behaviorSupportedTouch/pointer swipe is on by default and is the primary mobile interaction.

API reference

indexNone
number

Controlled active slide index.

defaultIndex0
number

Uncontrolled initial index.

onIndexChangeNone
(index: number) => void

Fires when the active slide changes.

controlsfalse
boolean

Overlay prev/next arrow buttons.

indicatorstrue
boolean

Show the bottom dot indicators.

slidesPerView1
number | { base; sm; md; lg }

Slides visible per view, fixed or per breakpoint.

autoplaytrue
boolean

Advance automatically.

autoplayInterval3000
number

Milliseconds between advances.

looptrue
boolean

Wrap seamlessly past the ends.

swipetrue
boolean

Allow touch/pointer swiping.

classNameNone
string

Additional classes applied to the component root.