# Slider (/ui/components/react/slider)



<Playground component="slider" />

## Usage [#usage]

```tsx
import { Slider } from '@appica/ui-react/slider'
```

```tsx
<Slider defaultValue={50} thumbAriaLabel="Volume" />
```

`Slider` renders the track, fill, and thumb(s) for you — you don't compose parts. The number of thumbs follows the value: a single `number` renders one thumb, an array renders one per entry (a range). Use it uncontrolled with `defaultValue`, or controlled with `value` + `onValueChange`.

A thumb has no visible text, so give it a name with `thumbAriaLabel` (a string, or a `(index) => string` function for ranges). By default a tooltip shows the current value while a thumb is hovered, focused, or dragged.

## Examples [#examples]

### Range [#range]

Pass an array `defaultValue` (or `value`) to get one thumb per entry. The fill spans between them. `thumbAriaLabel` can be a function of the thumb index to name each end.

```tsx
'use client'

import { Slider } from '@appica/ui-react/slider'

export default function SliderRange() {
  return (
    <Slider className="max-w-70" defaultValue={[25, 75]} thumbAriaLabel={(i) => (i === 0 ? 'Minimum' : 'Maximum')} />
  )
}
```

### Steps [#steps]

`step` sets the granularity; `min` and `max` set the bounds. Arrow keys move by `step`, **Shift**/**Page Up·Down** by `largeStep`.

```tsx
import { Slider } from '@appica/ui-react/slider'

export default function SliderSteps() {
  return (
    <Slider
      className="max-w-70"
      defaultValue={40}
      min={0}
      max={100}
      step={10}
      tooltipVisibility="always"
      thumbAriaLabel="Amount"
    />
  )
}
```

### Tooltip visibility [#tooltip-visibility]

`tooltipVisibility` controls the value bubble: `auto` (default — on hover/focus/drag), `always` (pinned), or `never`.

```tsx
import { Slider } from '@appica/ui-react/slider'

export default function SliderTooltip() {
  return (
    <div className="flex w-full max-w-70 flex-col gap-8">
      <Slider defaultValue={30} tooltipVisibility="auto" thumbAriaLabel="Auto" />
      <Slider defaultValue={50} tooltipVisibility="always" thumbAriaLabel="Always" />
      <Slider defaultValue={70} tooltipVisibility="never" thumbAriaLabel="Never" />
    </div>
  )
}
```

### Vertical [#vertical]

Pass `orientation="vertical"` to lay the slider out top-to-bottom (it fills its container's height, with a sensible minimum). Works for single and range sliders.

```tsx
'use client'

import { Slider } from '@appica/ui-react/slider'

export default function SliderVertical() {
  return (
    <div className="flex h-48 items-center gap-10">
      <Slider orientation="vertical" defaultValue={60} thumbAriaLabel="Volume" />
      <Slider orientation="vertical" defaultValue={[20, 80]} thumbAriaLabel={(i) => (i === 0 ? 'Min' : 'Max')} />
    </div>
  )
}
```

### Controlled [#controlled]

Drive the value with `value` + `onValueChange` to react to every move. Here three sliders compose an RGB picker, each channel feeding a live color swatch.

```tsx
'use client'

import { useState } from 'react'
import { Slider } from '@appica/ui-react/slider'

const CHANNELS = [
  { key: 'r', label: 'R' },
  { key: 'g', label: 'G' },
  { key: 'b', label: 'B' },
] as const

export default function SliderControlled() {
  const [rgb, setRgb] = useState({ r: 99, g: 102, b: 241 })
  const color = `#${[rgb.r, rgb.g, rgb.b].map((c) => c.toString(16).padStart(2, '0')).join('')}`

  return (
    <div className="flex w-full max-w-90 items-center gap-6">
      <div className="flex w-full max-w-64 flex-col gap-4">
        {CHANNELS.map(({ key, label }) => (
          <div key={key} className="flex items-center gap-3">
            <span className="text-foreground-muted w-3 text-sm font-medium">{label}</span>
            <Slider
              className="flex-1"
              value={rgb[key]}
              onValueChange={(value) => setRgb((prev) => ({ ...prev, [key]: value as number }))}
              min={0}
              max={255}
              tooltipVisibility="never"
              thumbAriaLabel={`${label} channel`}
            />
            <span className="text-foreground-muted w-8 text-end text-sm tabular-nums">{rgb[key]}</span>
          </div>
        ))}
      </div>
      <div className="flex shrink-0 flex-col items-center gap-2">
        <div className="border-border-muted size-20 rounded-lg border" style={{ backgroundColor: color }} aria-hidden />
        <span className="text-foreground-muted font-mono text-xs uppercase">{color}</span>
      </div>
    </div>
  )
}
```

### Disabled [#disabled]

`disabled` dims the slider and blocks interaction.

```tsx
import { Slider } from '@appica/ui-react/slider'

export default function SliderDisabled() {
  return <Slider className="max-w-70" defaultValue={50} disabled thumbAriaLabel="Disabled" />
}
```

## RTL [#rtl]

Every Appica UI component supports right-to-left layouts out of the box. Set the `dir` attribute on a container (commonly your `<html>` element) so CSS logical properties resolve correctly, and wrap your tree in `DirectionProvider` so direction-aware behavior — roving focus, popup placement, and the like — follows the same direction.

```tsx
import { DirectionProvider } from '@appica/ui-react/providers/direction-provider'

export default function RootLayout({ children }) {
  return (
    <html lang="ar" dir="rtl">
      <body>
        <DirectionProvider dir="rtl">{children}</DirectionProvider>
      </body>
    </html>
  )
}
```

The track fills from the end edge (right) and the arrow keys that increase the value flip with the direction. For setup details and caveats, see the [RTL guide](/ui/docs/react/rtl).

<RtlPreview component="slider" />

## API reference [#api-reference]

The `tooltipVisibility` and `thumbAriaLabel` props are Appica additions; every other prop is forwarded to [Base UI's Slider.Root](https://base-ui.com/react/components/slider).

| Prop                    | <ColMinWidth width="200">Type</ColMinWidth>        | Default        | <ColMinWidth width="220">Description</ColMinWidth>                 |
| ----------------------- | -------------------------------------------------- | -------------- | ------------------------------------------------------------------ |
| `tooltipVisibility`     | <Code>'auto' \| 'always' \| 'never'</Code>         | `'auto'`       | When the value tooltip shows — on interaction, always, or never.   |
| `thumbAriaLabel`        | <Code>string \| ((index: number) => string)</Code> | —              | Accessible name for each thumb. Use a function to name range ends. |
| `value`                 | <Code>number \| number\[]</Code>                   | —              | Controlled value (array = range). Pair with `onValueChange`.       |
| `defaultValue`          | <Code>number \| number\[]</Code>                   | —              | Uncontrolled initial value; its shape sets the number of thumbs.   |
| `onValueChange`         | <Code>(value, details) => void</Code>              | —              | Fires as the value changes (drag, keyboard).                       |
| `onValueCommitted`      | <Code>(value, details) => void</Code>              | —              | Fires when the change is finalized (pointer up / blur).            |
| `min`                   | `number`                                           | `0`            | Minimum value.                                                     |
| `max`                   | `number`                                           | `100`          | Maximum value.                                                     |
| `step`                  | `number`                                           | `1`            | Stepping granularity.                                              |
| `largeStep`             | `number`                                           | `10`           | Step for **Shift**+Arrow / **Page Up·Down**.                       |
| `minStepsBetweenValues` | `number`                                           | `0`            | Minimum gap (in steps) between range thumbs.                       |
| `orientation`           | <Code>'horizontal' \| 'vertical'</Code>            | `'horizontal'` | Layout direction.                                                  |
| `disabled`              | `boolean`                                          | `false`        | Disables interaction and dims the slider.                          |
| `name`                  | `string`                                           | —              | Field name submitted with a form.                                  |
| `format`                | `Intl.NumberFormatOptions`                         | —              | Formats the tooltip/value display (currency, percent, …).          |
| `className`             | <Code>string \| ((state) => string)</Code>         | —              | Extra classes on the root, merged via `tailwind-merge`.            |

`Slider` wraps Base UI's `Slider` (`Root`, `Control`, `Track`, `Indicator`, `Thumb`, `Value`) and forwards every remaining `Root` prop. It exposes `data-*` state attributes (`data-orientation`, `data-disabled`, `data-dragging`, …) on the root and its parts for styling.

## Accessibility [#accessibility]

* Each thumb is a `role="slider"` with `aria-valuenow`/`aria-valuemin`/`aria-valuemax`. Give it a name via `thumbAriaLabel`, or an external label associated with the control.
* Keyboard: &#x2A;*←/→*&#x2A; (or &#x2A;*↑/↓**) move by `step`, **Shift**+arrow and **Page Up·Down** move by `largeStep`, **Home/End** jump to min/max.
* `disabled` blocks interaction and exposes `data-disabled` for styling.
* The tooltip and thumb-scale animations honour `prefers-reduced-motion`.
