---
title: Carousel
description: A composable carousel built on embla-carousel with keyboard navigation, wheel gestures, and RTL support
---

## Overview

`Carousel` is a low-level composable carousel. It provides a context with scroll state and navigation functions that its child components (`CarouselBody`, `CarouselItem`, `CarouselNext`, `CarouselPrevious`) consume. Use it when you need full control over carousel layout and navigation UI.

## Installation

Using the CLI:

```bash
npx shadcn@latest add https://develop.trident-ui.pro.clubmed/r/carousel.json
```

Or manually, copy the following code into `/ui/carousel/Carousel.tsx`:

```tsx
'use client';
import type { ComponentProps, FunctionComponent } from 'react';

import { CarouselProvider } from './Carousel.context';

export const Carousel: FunctionComponent<ComponentProps<typeof CarouselProvider>> = ({
  children,
  ...props
}) => {
  return <CarouselProvider {...props}>{children}</CarouselProvider>;
};
```

## Usage

```tsx showLineNumbers
import { Carousel } from '@/ui/carousel/Carousel';
import { CarouselBody } from '@/ui/carousel/CarouselBody';
import { CarouselItem } from '@/ui/carousel/CarouselItem';
import { CarouselNext } from '@/ui/carousel/CarouselNext';
import { CarouselPrevious } from '@/ui/carousel/CarouselPrevious';

export function Component() {
  return (
    <Carousel className="relative isolate">
      <CarouselBody scrollClassName="auto-cols-full gap-20">
        {items.map((item, i) => (
          <CarouselItem index={i} key={i}>
            <div className="rounded-16 h-300 w-full">{item}</div>
          </CarouselItem>
        ))}
      </CarouselBody>
      <div className="pointer-events-none absolute inset-0 flex items-center justify-between px-12">
        <CarouselPrevious label="Previous" />
        <CarouselNext label="Next" />
      </div>
    </Carousel>
  );
}
```

## API Reference

### Carousel

| Prop          | Type              | Default     | Description                                     |
| ------------- | ----------------- | ----------- | ----------------------------------------------- |
| `options`     | `EmblaOptions`    | `undefined` | embla-carousel options (align, axis, etc.)      |
| `plugins`     | `EmblaPlugin[]`   | `[]`        | Additional embla plugins                        |
| `scrollValue` | `number`          | `undefined` | Pixel distance for scroll (overrides snap scroll) |
| `className`   | `string`          | `undefined` | Additional CSS classes on the outer container   |

### CarouselBody

| Prop              | Type     | Default     | Description                                        |
| ----------------- | -------- | ----------- | -------------------------------------------------- |
| `scrollClassName` | `string` | `undefined` | Classes applied to the inner scroll grid container |
| `className`       | `string` | `undefined` | Classes applied to the overflow wrapper            |

### CarouselItem

| Prop    | Type     | Default  | Description                                               |
| ------- | -------- | -------- | --------------------------------------------------------- |
| `index` | `number` | Required | Zero-based index; used to compute `is-visible`/`is-snapped` state |

### CarouselNext / CarouselPrevious

| Prop    | Type     | Default     | Description                             |
| ------- | -------- | ----------- | --------------------------------------- |
| `label` | `string` | Required    | Accessible label for the button (sr-only) |
| `icon`  | `string` | `undefined` | Override the default arrow icon         |

## Notes

- All components are marked `'use client'` — they use React hooks and browser APIs
- Keyboard navigation: `ArrowLeft` scrolls previous, `ArrowRight` scrolls next
- Wheel gesture scrolling is enabled by default via `embla-carousel-wheel-gestures`
- RTL is detected automatically from `document.documentElement.dir`
- `CarouselItem` adds `aria-hidden` and `inert` to slides not in view
- Default embla options: `align: "start"`, `skipSnaps: true`, `inViewThreshold: 0.85`
- The `useCarousel` hook is exported from `Carousel.context` for building custom sub-components
