Skip to main content

Carousel

MD3 (Expressive) carousel: a horizontally scrolling row of 28dp-rounded items. multi-browse and hero use Compose's keyline layout — items are masked (never scaled) as they move between large, medium and small sizes.

Multi-browse​

The default layout: one or more large items followed by medium and small (40–56dp) items, snapping to the start. itemWidth is the preferred large width; the actual size is fitted to the container.

Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
Item 7
Item 8
<Carousel aria-label="Photos" itemWidth={260} itemHeight={200}>
{photos.map((photo) => (
<CarouselItem key={photo.id}>
<img src={photo.src} alt={photo.alt} />
</CarouselItem>
))}
</Carousel>

Images keep their size and are clipped by the item's mask.

Uncontained​

Single-size items that flow past the edge of the container and scroll freely, without snapping.

Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
Item 7
Item 8
<Carousel aria-label="Photos" variant="uncontained" itemWidth={200}>
…
</Carousel>

Hero​

A large, centered item with a small item on each side, snapping to the center (the first and last items align to the start and end).

Item 3
Item 4
Item 5
Item 6
Item 7
Item 8
<Carousel aria-label="Featured" variant="hero" itemHeight={240}>
…
</Carousel>

Interactive items​

onClick renders an item as a <button> and href as an <a>, with the state layer, ripple and focus ring. Tab and Left / Right move between items, and a focused item scrolls into the large position. disabled dims an interactive item and removes it from the Tab order.

<Carousel aria-label="Albums">
{albums.map((album) => (
<CarouselItem key={album.id} aria-label={album.title} onClick={() => play(album)}>
<img src={album.cover} alt="" />
</CarouselItem>
))}
</Carousel>

Localized labels​

Each item is announced with its position — "2 of 7" by default. Pass getItemLabel to localize it, and roleDescriptionLabel / itemRoleDescriptionLabel for the announced "carousel" / "slide" role descriptions.

Foto 1
Foto 2
Foto 3
Foto 4
Foto 5
Foto 6
<Carousel
aria-label="Fotos"
getItemLabel={(position, count) => `${position} von ${count}`}
roleDescriptionLabel="Karussell"
itemRoleDescriptionLabel="Folie"
>
…
</Carousel>

Accessibility​

  • Always give the carousel an aria-label: it is a group announced with the role description "carousel".
  • Non-interactive items are groups announced as slides and named by their position ("2 of 7"); interactive items get their position as the accessible description, so name them with their content or aria-label.
  • With interactive items, Tab and Left / Right (mirrored in RTL) move between items. Without them, the scroll container itself is focusable so keyboard users can still scroll it.
  • Under prefers-reduced-motion: reduce, every item keeps one size (no keyline masking) and focus scrolling is instant.
  • Put nested interactive content only in non-interactive items.

Props​

PropTypeDefaultDescription
aria-labelstring—Accessible name of the carousel. Required in practice — the container is a `group` announced as "carousel".
childrenReactNode—`CarouselItem`s.
getItemLabel((position: number, count: number) => string)(position: number, count: number) => `${position} of ${count}`Accessible label of each item's position, announced with the item (non-interactive items: the slide's name; `onClick` / `href` items: its description). `position` is 1-based.
itemHeightnumber200Item height in px.
itemRoleDescriptionLabelstringslideRole description announced for each non-interactive item (`aria-roledescription`).
itemWidthnumber260 (`hero`: fills the container beside its small items)Preferred (large) item width in px. `multi-browse` / `hero` fit the actual large size to the container (Compose `preferredItemWidth`); `uncontained` uses it as-is.
roleDescriptionLabelstringcarouselRole description announced for the carousel container (`aria-roledescription`).
spacingnumber8Gap between items in px.
variantenummulti-browseLayout (m3.material.io / Compose `HorizontalMultiBrowseCarousel`, `HorizontalUncontainedCarousel`, `HorizontalCenteredHeroCarousel`): - `multi-browse` — large, medium and small (40–56dp) items, start-snapped; - `uncontained` — single-size items flowing past the edge, free scrolling; - `hero` — centered hero: a large item with a small item on each side, center-snapped (the first / last item align to the start / end).

CarouselItem​

PropTypeDefaultDescription
disabledbooleanfalseDisable an interactive (`onClick` / `href`) item: dimmed, not focusable.
downloadany—Download hint (with `href`).
hrefstring—Makes the item a link (`<a>`): activated with Enter, opens in new tabs.
relstring—Link relationship (with `href`).
targetHTMLAttributeAnchorTarget—Link target (with `href`).