Skip to content
Component

Carousel

One carousel for single slides, responsive multi-slide tracks, a continuous ticker, and a 3D coverflow. Transform-based slides, progress dots, seamless looping, swipe, and keyboard support, with no external dependency. Autoplay is opt-in.

Basic autoplay

Autoplay is opt-in: pass autoplay to advance every 3000ms with no arrows. The active dot fills to show progress until the next slide.

  • autoplay defaults to false - self-starting motion is the page owner's WCAG 2.2.2 obligation
  • Autoplay pauses on hover, focus, touch, and when the tab is hidden, and never runs under prefers-reduced-motion

Configurable interval

Pass autoplay with autoplayInterval; the progress dot animation matches it.

Transition duration

transitionDuration sets how long each move takes, in milliseconds. Raised close to autoplayInterval, the track glides almost continuously instead of stepping and then pausing.

  • Defaults to the shared --fuji-duration-slow token

Arrow controls

Pass controls for prev/next arrows overlaid on the edges. The arrows are icon buttons on a translucent disc, so they read over any photo, and the indicator dots are 24px hit targets.

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.

Coverflow effect

effect='coverflow' centers the active slide with neighbours rotated away in 3D and scaled by distance; the fan follows the pointer while dragging and snaps on release. Arrow keys and controls work as in the default preset; slidesPerView is ignored.

  • Coverflow ignores slidesPerView - the active slide is always centered with its neighbours fanned behind it

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.
  • Slides that are images or links swipe like any other: the carousel cancels the browser's native drag on its viewport. Give a child its own draggable to opt back in.

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

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

children *None
React.ReactNode

The slides. Each child becomes one slide.

aria-labelNone
string

Defines a string value that labels the current element.

autoplayfalse
boolean

Advance automatically; paused on hover/focus/touch/tab-hidden, off under reduced motion. Defaults to **false** (since 0.3.0): self-starting motion over 5s is a WCAG 2.2.2 duty, so it's opt-in.

autoplayInterval3000
number

Milliseconds between autoplay advances. Default 3000.

continuousfalse
boolean

Always-moving CSS marquee instead of stepped slides; only `autoplayInterval` (ms per slide-width) and `slidesPerView` apply. Pauses on hover/focus, tab-hidden and reduced motion. Default false.

controlsfalse
boolean

Overlay prev/next arrow controls. Default false.

defaultIndex0
number

Uncontrolled initial index. Default 0.

effect"slide"
"slide" | "coverflow"

`"coverflow"` ports motion.dev's: a centred ~62% slide, neighbours rotated 20° and shrunk to 70%, faded edges, following the drag then snapping. Default `"slide"`.

indexNone
number

Controlled active slide index.

indicatorstrue
boolean

Bottom dot indicators. Default true.

looptrue
boolean

Wrap seamlessly past the ends. Default true.

onIndexChangeNone
((index: number) => void)

Called with the new index whenever the active slide changes, however it changed.

slidesPerView1
ResponsiveCount

Slides per view: a number or breakpoint map. Default 1 - or 1.6 with `effect="coverflow"`, where it sets the centre width (`100% / slidesPerView`; 1.6 is ~62%, 3 shows more fan; min 1).

swipetrue
boolean

Allow touch/pointer swiping. Default true.

transitionDurationNone
number

Per-slide transition length in ms. Defaults to the shared `--fuji-duration-slow` token. Raise it towards `autoplayInterval` for a continuous glide instead of a quick step and a long pause.

Also accepts everything from Omit<React.HTMLAttributes<HTMLDivElement>, "onChange">.

Coding agents read this reference, with its examples, from the version your project has installed, through the Fuji UI MCP server. Set up your agent