Carousel

Browses a sequence on a scroll-snap track. Buttons move one slide; the ends feather.

Flip

Use it for

  • A row of cards or images wider than the page, browsed one nudge at a time.
  • Scroll snap does the alignment; the buttons nudge by 80% of the width.

Not for

  • Autoplay or hero sliders; nothing moves on its own.
  • Essential content only reachable by scrolling; show it in the flow too.

Three ways in. They share one source, so the pixels match whichever you pick.

1. Import the package

npm install @noorddev/vlak-react

// once, next to your app's root
import "@noorddev/vlak-react/css";

import { Carousel, CarouselSlide } from "@noorddev/vlak-react";

Precompiled React and one stylesheet. No compiler to configure. Per-component imports work too: @noorddev/vlak-react/components/carousel.

2. Vendor the source

npx @noorddev/vlak-cli add carousel

The StyleX leaf lands in components/vlak/ with its dependencies, for your own compiler to own. See StyleX.

3. Through shadcn

npx shadcn add https://vlak.dev/r/carousel.json

The same registry item, installed by shadcn's CLI.

CSS only

<link rel="stylesheet" href="node_modules/@noorddev/vlak/css/vlak.css" />

No React. Link vlak.css and use the markup and classes below.

import { Carousel, CarouselSlide } from "@noorddev/vlak-react";

<Carousel aria-label="Case studies">
  {cases.map((c) => (
    <CarouselSlide key={c.id}>
      <CaseCard {...c} />
    </CarouselSlide>
  ))}
</Carousel>

Carousel

Native scroll snap; the buttons nudge. The track is the named, focusable carousel region.

PropTypeDefaultDescription
aria-labelstring"Carousel"Name of the carousel region.

Also accepts HTMLAttributes<HTMLDivElement>.

CarouselSlide

One slide: a named group, "n of N" unless you name it yourself.

Also accepts HTMLAttributes<HTMLDivElement>.

KeysDoes
TabFocuses the track, then the previous and next buttons
Arrow left, Arrow rightScrolls the focused track
Enter, SpaceNudges from the buttons
<div class="rs-carousel"><div class="rs-carousel-track"><div class="rs-carousel-slide">One</div><div class="rs-carousel-slide">Two</div><div class="rs-carousel-slide">Three</div></div><div class="rs-carousel-nav"><button class="rs-page"><svg viewBox="0 0 16 16" width="12" height="12" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter"><path d="M10.5 3.75 L5.5 8.25 L10.5 12.75" vector-effect="non-scaling-stroke"/></svg></button><button class="rs-page"><svg viewBox="0 0 16 16" width="12" height="12" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter"><path d="M5.5 3.75 L10.5 8.25 L5.5 12.75" vector-effect="non-scaling-stroke"/></svg></button></div></div>
.rs-carousel.rs-carousel-track.rs-carousel-slide.rs-carousel-nav.rs-carousel-icon.rs-carousel-page