---
id: "packages/active-ui/slider"
title: "Slider"
description: "A value or a range picked on a scale, with the keyboard or by dragging."
url: "/packages/active-ui/slider"
locale: "en"
revision: "5f991b5c8975be21"
---

# Slider

A slider picks a value where the position on a scale matters more than the exact number, such as a
share or a range of ages. With two values it picks a range. When the exact number matters, pair it
with text that shows the value, or use a [NumberField](/packages/active-ui/number-field).

**Example: One value, a range and disabled**

Client: Runs entirely in your browser. Source: `examples/ui/slider.tsx`.

```tsx
// apps/docs/examples/ui/slider.tsx
'use client';

import {useState} from 'react';

import {
  Field,
  FieldDescription,
  FieldLabel,
} from '@workspace/active-ui/components/primitives/field';
import {Slider} from '@workspace/active-ui/components/primitives/slider';

/**
 * A value picked on a scale: one thumb, a range with two thumbs and a step,
 * and a disabled slider. The value is an array, one number per thumb; the
 * current value is shown as text, because a slider alone does not tell the
 * reader the exact number.
 */
export default function SliderExample() {
  const [share, setShare] = useState([40]);
  const [range, setRange] = useState([25, 65]);
  return (
    <div className="grid w-full max-w-md gap-8">
      <Field>
        <FieldLabel id="slider-share-label">Equity share</FieldLabel>
        <Slider
          value={share}
          onValueChange={(value) => setShare(value as number[])}
          aria-labelledby="slider-share-label"
        />
        <FieldDescription>{share[0]}% of the contributions</FieldDescription>
      </Field>
      <Field>
        <FieldLabel id="slider-age-label">Age range</FieldLabel>
        <Slider
          value={range}
          min={18}
          max={67}
          step={1}
          onValueChange={(value) => setRange(value as number[])}
          aria-labelledby="slider-age-label"
        />
        <FieldDescription>
          From {range[0]} to {range[1]} years
        </FieldDescription>
      </Field>
      {/* The label keeps full contrast: it names a control that is not
          associated with it by `for`, so it is not exempt as disabled text. */}
      <Field>
        <FieldLabel id="slider-fixed-label">Employer share (fixed)</FieldLabel>
        <Slider
          defaultValue={[50]}
          disabled
          aria-labelledby="slider-fixed-label"
        />
      </Field>
    </div>
  );
}
```

## Usage

```tsx
import {Slider} from '@workspace/active-ui/components/primitives/slider';

<Slider
  value={share}
  onValueChange={(value) => setShare(value as number[])}
  aria-labelledby="share-label"
/>;
```

## Values and thumbs

- **One thumb per value.** A number (`40`) or an array of one (`[40]`) gives one thumb; an array
  of two (`[25, 65]`) a range. `onValueChange` reports the value in the shape you passed.
- Without `value` or `defaultValue` it renders two thumbs, at `min` and `max`: a range.
- `min` (default `0`), `max` (default `100`) and `step` (default `1`) set the scale.
- `onValueChange` reports while the reader drags; `onValueCommitted` once, when they let go.
  Use the second for work that should not run on every step, such as a request.

## What your application provides

- A label: `aria-labelledby` pointing at visible text, or `aria-label`. Both reach the input
  of every thumb; a range has one name for both thumbs.
- The value as text, when the reader needs the number. The slider announces it to assistive
  technology (`aria-valuenow`), but shows no number itself.
- `locale`, and `format` for a value that is more than a plain number (a percentage, a
  currency). Base UI formats the announced value with `Intl.NumberFormat`; without `locale` it
  uses the browser's language, not the one your application presents.

## Accessibility and keyboard

- Each thumb is a tab stop. The arrow keys move it by `step`; `Page Up` and `Page Down` by a
  larger step (`largeStep`, default `10`); `Home` and `End` to `min` and `max`.
- Each thumb is a `role="slider"` input with its minimum, maximum and value.
- When two thumbs meet while dragging, the one you drag pushes the other along
  (`thumbCollisionBehavior`, default `push`; `swap` and `none` are the alternatives).

## API

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `defaultValue` | `number \| readonly number[]` | no | The uncontrolled value of the slider when it's initially rendered.  To render a controlled slider, use the `value` prop instead. |
| `disabled` | `boolean` | no | Whether the slider should ignore user interaction. |
| `form` | `string` | no | Identifies the form that owns the slider inputs. Useful when the slider is rendered outside the form. |
| `format` | `Intl.NumberFormatOptions` | no | Options to format the value. |
| `largeStep` | `number` | no | The granularity with which the slider can step through values when using Page Up/Page Down or Shift + Arrow Up/Arrow Down. |
| `locale` | `Intl.LocalesArgument` | no | The locale used by `Intl.NumberFormat` when formatting the value. Defaults to the user's runtime locale. |
| `max` | `number` | no | The maximum allowed value of the slider. Should not be equal to min. |
| `min` | `number` | no | The minimum allowed value of the slider. Should not be equal to max. |
| `minStepsBetweenValues` | `number` | no | The minimum steps between values in a range slider. |
| `name` | `string` | no | Identifies the field when a form is submitted. |
| `onValueChange` | `((value: number \| readonly number[], eventDetails: SliderRoot.ChangeEventDetails) => void)` | no | Callback function that is fired when the slider's value changed. Receives the new value as the first argument; the originating event is available as `eventDetails.event`. The value is also reflected on `eventDetails.event.target.value` for form integration.  The `eventDetails.reason` indicates what triggered the change:  - `'input-change'` when the hidden range input emits a change event (for example, via form integration) - `'track-press'` when the control track is pressed - `'drag'` while dragging a thumb - `'keyboard'` for keyboard input - `'none'` when the change is triggered without a specific interaction |
| `onValueCommitted` | `((value: number \| readonly number[], eventDetails: SliderRoot.CommitEventDetails) => void)` | no | Callback function that is fired when a value change is committed. Does not fire if the value did not change, or if the change was canceled. **Warning**: This is a generic event, not a change event.  The `eventDetails.reason` indicates what triggered the commit:  - `'drag'` while dragging a thumb - `'track-press'` when the control track is pressed - `'keyboard'` for keyboard input - `'input-change'` when the hidden range input emits a change event (for example, via form integration) - `'none'` when the commit occurs without a specific interaction |
| `orientation` | `Orientation` | no | The component orientation. |
| `render` | `ComponentRenderFn<HTMLProps, SliderRootState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |
| `step` | `number` | no | The granularity with which the slider can step through values. (A "discrete" slider.) The `min` prop serves as the origin for the valid values. We recommend (max - min) to be evenly divisible by the step. |
| `thumbAlignment` | `"center" \| "edge" \| "edge-client-only"` | no | How the thumb(s) are aligned relative to `Slider.Control` when the value is at `min` or `max`: - `center`: The center of the thumb is aligned with the control edge - `edge`: The thumb is inset within the control such that its edge is aligned with the control edge - `edge-client-only`: Same as `edge` but renders after React hydration on the client, reducing bundle size in return |
| `thumbCollisionBehavior` | `"none" \| "push" \| "swap"` | no | Controls how thumbs behave when they collide during pointer interactions.  - `'push'` (default): Thumbs push each other without restoring their previous positions when dragged back. - `'swap'`: Thumbs swap places when dragged past each other. - `'none'`: Thumbs cannot move past each other; excess movement is ignored. |
| `value` | `number \| readonly number[]` | no | The value of the slider. For range sliders, provide an array with one value per thumb. |

> [!NOTE]
> **Based on shadcn/ui**
>
> This page adapts the Slider documentation of shadcn/ui (MIT licence) to this
> package; see [Third-party notices](/third-party-notices).
