# Slider

Choose a numeric value or range by moving one or more thumbs.

Volume slider with value

[Complete TSX file](/examples/slider/default.tsx)

```tsx
"use client";

import { Field, FieldLabel } from "@/siteplane/components/field";
import { Slider, SliderValue } from "@/siteplane/components/slider";

export default function Example() {
  return (
    <Field className="w-full max-w-sm">
      <Slider defaultValue={40}>
        <div className="flex items-center justify-between gap-2">
          <FieldLabel>Volume</FieldLabel>
          <SliderValue />
        </div>
      </Slider>
    </Field>
  );
}
```

## Installation

Complete [one-time setup](/docs/installation/) first.

```bash
pnpm dlx shadcn@4.18.0 add @siteplane/slider
```

With npm: `npx shadcn@4.18.0 add @siteplane/slider`. [Registry JSON](/registry/slider.json) lists source files and dependencies.

For this page’s examples, also install:

```bash
pnpm dlx shadcn@4.18.0 add @siteplane/button @siteplane/form @siteplane/input
```

Field is installed with Input.

## Usage

```tsx
import { Slider } from "@/siteplane/components/slider";

<Slider defaultValue={40} aria-label="Volume" />;
```

## Composition

```text
Slider
├── SliderValue (optional child)
└── built-in control, track and thumbs
```

## Controlled range

An array creates one thumb per value. The application owns the selected range.

Controlled price range

[Complete TSX file](/examples/slider/controlled.tsx)

```tsx
"use client";

import { Field, FieldLabel } from "@/siteplane/components/field";
import { Slider, SliderValue } from "@/siteplane/components/slider";
import { useState } from "react";

export default function Example() {
  const [value, setValue] = useState<number | readonly number[]>([200, 800]);
  return (
    <Field className="w-full max-w-sm">
      <Slider
        max={1000}
        min={0}
        onValueChange={setValue}
        step={50}
        value={value}
      >
        <div className="flex items-center justify-between gap-2">
          <FieldLabel>Price range</FieldLabel>
          <SliderValue />
        </div>
      </Slider>
    </Field>
  );
}
```

## Scale labels

Use `labels` to mark positions on the scale; they sit below the track and are hidden for vertical sliders. Each label stays on one line, and labels at the ends stay within the slider’s edges. They don’t name the slider, so still give it a label.

Storage slider with scale labels

[Complete TSX file](/examples/slider/labels.tsx)

```tsx
"use client";

import { Field, FieldLabel } from "@/siteplane/components/field";
import { Slider } from "@/siteplane/components/slider";

export default function Example() {
  return (
    <Field className="w-full max-w-sm">
      <FieldLabel>Storage limit</FieldLabel>
      <Slider
        defaultValue={15}
        labels={[
          { value: 5, label: "5 GB" },
          { value: 20, label: "20 GB" },
          { value: 35, label: "35 GB" },
        ]}
        max={35}
        min={5}
      />
    </Field>
  );
}
```

## Orientation and disabled

Vertical controls need enough space for their minimum height.

Slider orientation and disabled state

[Complete TSX file](/examples/slider/appearance.tsx)

```tsx
"use client";

import { Field, FieldLabel } from "@/siteplane/components/field";
import { Slider } from "@/siteplane/components/slider";

export default function Example() {
  return (
    <div className="grid w-full max-w-sm gap-4">
      <Field>
        <FieldLabel>Volume, vertical</FieldLabel>
        <Slider defaultValue={40} orientation="vertical" />
      </Field>
      <Field disabled>
        <FieldLabel>Volume, disabled</FieldLabel>
        <Slider defaultValue={40} />
      </Field>
    </div>
  );
}
```

## Sizes

Omit `size` to inherit the provider's control size. The track and thumbs grow with the size; the value and the scale labels keep 12px text at every size.

Slider sizes

[Complete TSX file](/examples/slider/sizes.tsx)

```tsx
"use client";

import { Field, FieldLabel } from "@/siteplane/components/field";
import { Slider } from "@/siteplane/components/slider";

const sizes = [
  { name: "small", size: "sm" },
  { name: "medium", size: "md" },
  { name: "large", size: "lg" },
  { name: "extra large", size: "xl" },
] as const;

export default function Example() {
  return (
    <div className="grid w-full max-w-sm gap-4">
      {sizes.map(({ name, size }) => (
        <Field key={size}>
          <FieldLabel>Volume, {name}</FieldLabel>
          <Slider defaultValue={40} size={size} />
        </Field>
      ))}
    </div>
  );
}
```

## Form integration

Give the surrounding `Field` a `name` to submit the slider value with the form; a range submits one entry per thumb.

Session timeout in a settings form

[Complete TSX file](/examples/slider/form.tsx)

```tsx
"use client";

import { Button } from "@/siteplane/components/button";
import {
  Field,
  FieldDescription,
  FieldLabel,
} from "@/siteplane/components/field";
import { Form } from "@/siteplane/components/form";
import { Slider, SliderValue } from "@/siteplane/components/slider";

export default function Example() {
  return (
    <Form
      className="grid w-full max-w-sm gap-3"
      onSubmit={(event) => {
        event.preventDefault();
      }}
    >
      <Field name="sessionTimeout">
        <Slider defaultValue={30} max={120} min={15} step={15}>
          <div className="flex items-center justify-between gap-2">
            <FieldLabel>Session timeout</FieldLabel>
            <SliderValue />
          </div>
        </Slider>
        <FieldDescription>
          Minutes of inactivity before members are signed out.
        </FieldDescription>
      </Field>
      <Button type="submit">Save</Button>
    </Form>
  );
}
```

## API reference

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| Slider.size | sm \| md \| lg \| xl | provider / lg | Control, track, thumb and label sizes. |
| Slider.labels | readonly {label: ReactNode; value: number}[] | unset | Horizontal scale labels on one line; positions clamp to the range and end labels stay within the slider. |
| Slider.value / defaultValue | number \| number[] | unset (one thumb at min) | Controlled or initial values; one thumb per value. |
| Slider.min / max | number | 0 / 100 | Range bounds. |
| Slider.orientation | horizontal \| vertical | horizontal | Control direction. |
| Slider.thumbAlignment | foundation alignment | edge | Forwarded override can replace the edge default. |
| Slider.aria-label | string | unset | Applied to every generated thumb. |
| Slider.onValueChange | (value, eventDetails) => void | unset | Live edit callback. |
| Slider.onValueCommitted | (value, eventDetails) => void | unset | Committed edit callback. |
| SliderValue.size | sm \| md \| lg \| xl | root / provider / lg | Size context of the value. Its text is 12px at every size, like the scale labels. |
`SliderProps`, `SliderValueProps` and `SliderPrimitive` are exported. Slider
always renders its own control/track/indicator/thumbs; children supplement them,
not replace them. SliderValue formats the current value using the [Base UI
Slider](https://base-ui.com/react/components/slider) Value contract. Other step,
form, disabled, keyboard and validation props are inherited. The wrapper does
not expose a separate label per thumb; the supplied aria-label is repeated.
Scale labels are not public components.

[Read the installed source](/source/slider).

## Usage notes

Arrow keys adjust the focused thumb; Home/End move to bounds. Do not use scale labels as the only accessible name. Use NumberField if precise typed entry is necessary. The application owns business validation and persistence.
