Marble UI
← Components

Carousel

Preview: Single

Native scroll-snap carousel: swipe/trackpad scrolling for free, arrow buttons and dots for mouse/keyboard. Slides are a labelled group each ("2 of 5"), following the WAI carousel pattern.

Import

import { Carousel } from '@marbleui/ui'

Props

Accessibility

Keyboard interaction, focus management and ARIA semantics come from the underlying primitive: React Aria Button. Follow the primitive's docs for the full keyboard map.

Usage notes

Horizontally scrolling set of slides with arrows and dots. For a few always-visible items use a plain grid; for tabbed switching of content use Tabs.

<Carousel aria-label="Featured projects" perView={2} loop>
  <ProjectCard />
  <ProjectCard />
  <ProjectCard />
</Carousel>
  • aria-label is required and names the carousel region. Each child becomes a slide labelled “n of N”.
  • Built on native scroll-snap, so touch and trackpad swiping work. perView is 1, 2 or 3; gap is in pixels; showArrows and showDots default to true.
  • With loop, the arrows wrap around; without it they disable (and hide) at the ends.
  • Uncontrolled only: there is no index or change callback.
Example source · show
import { Carousel } from './carousel'

const slides = [
  { title: 'Plan', hue: 'from-indigo-500 to-violet-500' },
  { title: 'Build', hue: 'from-sky-500 to-cyan-400' },
  { title: 'Ship', hue: 'from-emerald-500 to-lime-400' },
  { title: 'Measure', hue: 'from-amber-500 to-orange-500' },
  { title: 'Repeat', hue: 'from-rose-500 to-pink-500' },
]

const Slide = ({ title, hue, tall }: { title: string; hue: string; tall?: boolean }) => (
  <div className={`grid ${tall ? 'h-56' : 'h-36'} place-items-center rounded-xl bg-gradient-to-br ${hue} text-xl font-semibold text-white`}>{title}</div>
)

export const styles = {
  Single: () => (
    <Carousel aria-label="Workflow" className="w-[28rem]">
      {slides.map((s) => <Slide key={s.title} {...s} tall />)}
    </Carousel>
  ),
  'Three up': () => (
    <Carousel aria-label="Workflow" perView={3} className="w-[36rem]">
      {slides.map((s) => <Slide key={s.title} {...s} />)}
    </Carousel>
  ),
  Loop: () => (
    <Carousel aria-label="Workflow" perView={2} loop showDots={false} className="w-[30rem]">
      {slides.map((s) => <Slide key={s.title} {...s} />)}
    </Carousel>
  ),
}

Source: packages/ui/src/carousel/carousel.tsx

Catalog

Category
data-display
Priority
Core
Primitive
custom