Carousel
A carousel with motion and swipe built using Embla.
Powered by
Installation
npx shadcn@latest add @redpanda/carouselpnpm dlx shadcn@latest add @redpanda/carouselyarn dlx shadcn@latest add @redpanda/carouselbunx shadcn@latest add @redpanda/carouselnubx shadcn@latest add @redpanda/carouselaube dlx shadcn@latest add @redpanda/carouselUsage
Carousel displays a scrollable, swipeable collection of slides with previous/next controls and keyboard arrow navigation.
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious,} from '@/components/redpanda-ui/carousel';<Carousel> <CarouselContent> <CarouselItem>...</CarouselItem> <CarouselItem>...</CarouselItem> <CarouselItem>...</CarouselItem> </CarouselContent> <CarouselPrevious /> <CarouselNext /></Carousel>Anatomy
Carousel Structure:┌─────────────────────────────────────────────────────┐│ <Carousel> (region, keyboard navigation) ││ ┌─────────────────────────────────────────────────┐ ││ │ <CarouselContent> (scroll viewport) │ ││ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ ││ │ │<CarouselItem>│ │<CarouselItem>│ │<CarouselItem>│ │ ││ │ │ (slide) │ │ (slide) │ │ (slide) │ │ ││ │ └─────────────┘ └─────────────┘ └─────────────┘ │ ││ └─────────────────────────────────────────────────┘ ││ <CarouselPrevious /> <CarouselNext /> │└─────────────────────────────────────────────────────┘Slot Guidelines
CarouselContent
Purpose: The scroll viewport that holds all slides.
Best practices:
- Set
gap="sm"orgap="md"onCarouselto coordinate track and item spacing. The default ismd(16px);smis 4px. - For vertical carousels, set an explicit viewport height (for example,
h-50).
<Carousel gap="sm"> <CarouselContent> <CarouselItem>...</CarouselItem> </CarouselContent></Carousel>CarouselItem
Purpose: A single slide. Has role="group" and aria-roledescription="slide".
Best practices:
- Use
basis-*utilities to control how many slides are visible at once - Keep slide content self-contained; avoid nested pressables inside clickable slides
{ /* 33% of the carousel width */}<CarouselItem className="basis-1/3">...</CarouselItem>;CarouselPrevious / CarouselNext
Purpose: Navigation buttons. They render the registry Button and accept all of its props (variant, size, testId, …). They disable automatically when the carousel cannot scroll further.
<CarouselPrevious variant="ghost" /><CarouselNext variant="ghost" />Props
Carousel
| Prop | Type | Default | Description |
|---|---|---|---|
opts |
CarouselOptions |
- | Embla options |
plugins |
CarouselPlugin |
- | Embla plugins such as Autoplay |
gap |
'sm' | 'md' |
'md' |
Coordinated slide spacing: 4px or 16px |
orientation |
'horizontal' | 'vertical' |
'horizontal' |
Scroll axis |
setApi |
(api: CarouselApi) => void |
- | Receives the Embla API instance for imperative control |
testId |
string |
- | Sets data-testid for testing |
Options
Options are passed to Embla via the opts prop:
<Carousel opts={{ align: 'start', loop: true, }}> ...</Carousel>API
Use the setApi prop with CarouselApi state to listen to events and control the carousel imperatively:
import { Carousel, type CarouselApi } from '@/components/redpanda-ui/carousel';
function Example() { const [api, setApi] = useState<CarouselApi>();
useEffect(() => { if (!api) { return; }
api.on('select', () => { // Do something on select }); }, [api]);
return <Carousel setApi={setApi}>...</Carousel>;}Plugins
Plugins such as Autoplay are passed via the plugins prop:
import Autoplay from 'embla-carousel-autoplay';
<Carousel plugins={[Autoplay({ delay: 2000 })]}>...</Carousel>;Examples
Basic
Sizes
Use the basis-* utility on <CarouselItem /> to size slides.
Vertical
Set orientation="vertical" and give <CarouselContent /> an explicit height.
With API
Accessibility
- The carousel root has
role="region"andaria-roledescription="carousel" - Each slide has
role="group"andaria-roledescription="slide" - Left/Right arrow keys scroll to the previous/next slide when focus is inside the carousel
- Navigation buttons include screen-reader-only labels and disable when scrolling is not possible
Credits
- We take our inspiration from Shadcn UI for the carousel component and style.
- Built on top of Embla Carousel.