Mosmox UI
Current version
GitHubnpmjs.com
Content & Media

Carousel

Accessible, responsive, touch-friendly content carousel with autoplay, looping, multiple views, and vertical layouts.

Carousel

Carousel displays a sequence of related items in a navigable viewport. It supports horizontal and vertical layouts, slide/fade/scale effects, multiple visible slides, looping, autoplay, pointer dragging, keyboard navigation, dots, thumbnails, progress, lazy images, disabled items, and custom rendering.

See the live Carousel showcase.

Basic usage

import { Carousel } from '@mosmox/ui';

const stories = [
  { key: 'one', title: 'First story', description: 'Short supporting copy.' },
  { key: 'two', title: 'Second story', description: 'Another item in the sequence.' },
];

<Carousel aria-label="Featured stories" items={stories} />

items may contain text, images, or arbitrary React content. Every item requires a stable, unique key. The built-in renderer uses eyebrow, badge, title, description, content, image, and alt. For completely custom markup, use renderItem.

Variants and effects

<Carousel items={stories} variant="default" effect="slide" />
<Carousel items={stories} variant="overlay" effect="fade" />
<Carousel items={products} variant="cards" effect="scale" slidesPerView={3} />
<Carousel items={articles} variant="minimal" />
<Carousel items={recommendations} variant="peek" />
OptionValuesDescription
variantdefaultBordered content cards; default.
overlayImage-focused cards with gradient content overlay.
minimalRemoves card chrome for editorial layouts.
cardsAdds elevation to each slide.
peekShows part of the next slide as a discovery cue.
effectslideTranslates the track; default effect.
fadeFades between full-width slides.
scaleScales the active slide and reduces opacity on inactive slides.
<Carousel
  aria-label="Travel destinations"
  items={destinations}
  showArrows
  showDots
  showProgress
  showThumbnails
  thumbnailSize={72}
  previousLabel="Previous destination"
  nextLabel="Next destination"
  dotLabel={(index, item) => `Open ${item.title ?? `slide ${index + 1}`}`}
/>
  • showArrows renders Previous and Next buttons; default true.
  • showDots renders tab-like position buttons; default true.
  • showProgress renders a progress bar; default false.
  • showThumbnails renders image thumbnail tabs; default false.
  • thumbnailSize sets thumbnail width and height in pixels; default 64.
  • previousLabel and nextLabel provide accessible arrow labels.
  • dotLabel creates accessible labels for dots and thumbnails.

The controls use the same navigation state as dragging and keyboard input. With loop={false}, the arrow at the first or last valid position becomes disabled.

Autoplay and interaction

<Carousel
  items={stories}
  autoplay
  interval={4500}
  pauseOnHover
  pauseOnFocus
  pauseOnInteraction
  loop
/>

Autoplay advances only when there are at least two items and interval is greater than zero. The play/pause button appears when autoplay is enabled. Hover, focus, and pointer interaction pause playback according to the corresponding options. The transition also respects prefers-reduced-motion.

draggable enables pointer/touch gestures and is true by default. A horizontal or vertical gesture of approximately 40px navigates one position. Pointer dragging ignores interactive controls such as arrows, dots, and thumbnails, so those controls remain clickable.

Multiple slides and orientation

<Carousel items={products} slidesPerView={3} gap={24} loop={false} />
<Carousel items={updates} orientation="vertical" showDots={false} />
  • orientation="horizontal" uses Left/Right keys and horizontal dragging; default.
  • orientation="vertical" uses Up/Down keys and vertical dragging.
  • slidesPerView is floored, clamped to at least 1, and limited to the number of items.
  • gap accepts a number in pixels or any CSS length string.
  • The number of navigable positions is items.length - visibleSlides + 1 (at least one). Dots and status use this number.

Controlled and uncontrolled state

const [index, setIndex] = useState(0);

<Carousel
  activeIndex={index}
  items={stories}
  onChange={(nextIndex, item) => {
    setIndex(nextIndex);
    analytics.track('carousel-change', { key: item.key });
  }}
/>

Use activeIndex for controlled state. Without it, defaultActiveIndex initializes the internal position. onChange runs after a valid arrow, dot, thumbnail, keyboard, drag, autoplay, or programmatic prop change navigation. The callback receives the new index and item.

Disabled items receive a disabled visual state and are skipped by navigation when possible:

<Carousel items={[
  { key: 'available', title: 'Available' },
  { key: 'unavailable', title: 'Unavailable', disabled: true },
  { key: 'next', title: 'Next item' },
]} />

Custom slide rendering

<Carousel
  aria-label="Editorial stories"
  items={stories}
  variant="minimal"
  renderItem={(item, index, state) => (
    <article data-active={state.active}>
      <span>0{index + 1}</span>
      <h3>{item.title}</h3>
      <p>{item.description}</p>
      <small>{state.visible ? 'Visible' : 'Hidden'}</small>
    </article>
  )}
/>

renderItem replaces the built-in item markup. Its state contains active, the item index, and whether the item is currently visible.

Images and lazy loading

<Carousel
  items={photos}
  lazy
  thumbnailSize={80}
  showThumbnails
/>

When lazy is true, non-visible images use native loading="lazy"; visible images load eagerly. image is the source URL and alt is its alternative text. For decorative images, provide an empty alt string.

API reference

CarouselItem

PropertyTypeDescription
keystringRequired stable identity.
contentReactNodeArbitrary content after the description.
labelReactNodeAccessible slide label when it is a string.
eyebrowReactNodeSmall label above the title.
titleReactNodeOptional heading.
descriptionReactNodeSupporting text.
badgeReactNodeBadge content.
imagestringImage URL.
altstringAlternative text for the image.
disabledbooleanMarks the item disabled and causes navigation to skip it when possible.

CarouselRenderState

PropertyTypeDescription
activebooleanWhether this item is the current starting position.
indexnumberZero-based item index.
visiblebooleanWhether the item is currently visible in the viewport.

Props

PropTypeDefaultDescription
itemsCarouselItem[]requiredItems to display. Empty arrays render nothing.
activeIndexnumber—Controlled current starting position.
defaultActiveIndexnumber0Initial uncontrolled position.
onChange(index: number, item: CarouselItem) => void—Called after valid navigation.
orientation'horizontal' | 'vertical''horizontal'Track and gesture direction.
variant'default' | 'overlay' | 'minimal' | 'cards' | 'peek''default'Visual slide treatment.
effect'slide' | 'fade' | 'scale''slide'Transition effect.
slidesPerViewnumber1Number of visible slides.
gapnumber | string16Gap between slides; numbers are pixels.
loopbooleantrueWrap navigation at the first and last position.
autoplaybooleanfalseAutomatically advances the carousel.
intervalnumber5000Autoplay delay in milliseconds.
pauseOnHoverbooleantruePause while the viewport is hovered.
pauseOnFocusbooleantruePause while the viewport has focus.
pauseOnInteractionbooleantruePause while pointer dragging.
draggablebooleantrueEnable pointer/touch dragging.
showArrowsbooleantrueRender Previous and Next buttons.
showDotsbooleantrueRender dot navigation.
showProgressbooleanfalseRender the position progress bar.
showThumbnailsbooleanfalseRender thumbnail tab navigation.
thumbnailSizenumber64Thumbnail size in pixels.
transitionDurationnumber320Transition duration in milliseconds.
previousLabelstring'Previous slide'Accessible label for the Previous button.
nextLabelstring'Next slide'Accessible label for the Next button.
playLabelstring'Start autoplay'Accessible label when autoplay is paused.
pauseLabelstring'Pause autoplay'Accessible label when autoplay is running.
dotLabel(index: number, item: CarouselItem) => stringGo to slide NAccessible label factory for dots and thumbnails.
lazybooleanfalseNative lazy-load non-visible images.
renderItem(item, index, state) => ReactNode—Replace built-in slide markup.
classNamestring—Additional class on the root section.
styleCSSProperties—Inline styles on the root section.
aria-labelstring'Carousel'Accessible name of the carousel region.

Public methods

Carousel does not expose a forwarded imperative ref or public methods. Navigation is performed through its rendered controls, keyboard and pointer interactions, autoplay, or controlled activeIndex state. Use onChange to synchronize application state or trigger side effects after navigation.

Accessibility

Give each carousel a meaningful aria-label, especially when a page contains more than one. The component exposes carousel and group semantics, a live position status, labelled arrow controls, selectable dot/thumbnail tabs, keyboard navigation, focus styling, and reduced-motion-friendly transitions. Keep visible labels and alternative text meaningful when slides contain important information.