# Slider (/docs/slider)





## API Reference [#api-reference]

<BaseUIReference href="https://base-ui.com/react/components/slider" />

## Basic [#basic]

<Preview cssProperties="sliderPlaygroundCssProperties">
  <SliderExample />

  <Preview.Code>
    {`
          import {
            Slider,
            SliderLabel,
            SliderValue,
            SliderThumb,
          } from "moduix";

          export function SliderDemo() {
            return (
              <Slider defaultValue={40}>
                <SliderLabel>Volume</SliderLabel>
                <SliderValue>{([formattedValue]) => \`\${formattedValue}%\`}</SliderValue>
                <SliderThumb aria-label="Volume" />
              </Slider>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSSProperties>
    {(context) => <SliderCssPropertiesPanel {...context} />}
  </Preview.CSSProperties>

  <Preview.CSSPlayground>
    {(context) => <SliderCssPlaygroundPanel {...context} />}
  </Preview.CSSPlayground>
</Preview>

## Anatomy [#anatomy]

`Slider` renders the Base UI control, track, and indicator internally, so consumers only compose the
meaningful content: label, value, and thumbs. Add one `SliderThumb` for each value in the slider.

```text
Slider
├─ SliderLabel
├─ SliderValue
└─ internal control
   └─ internal track
      ├─ internal indicator
      └─ SliderThumb[index]
```

```tsx
<Slider defaultValue={40}>
  <SliderLabel>Volume</SliderLabel>
  <SliderValue>{([formattedValue]) => `${formattedValue}%`}</SliderValue>
  <SliderThumb aria-label="Volume" />
</Slider>
```

| Part          | Role                                                                                                        |
| ------------- | ----------------------------------------------------------------------------------------------------------- |
| `Slider`      | Root state machine. Controls value, range values, steps, orientation, disabled state, and form integration. |
| `SliderLabel` | Visible label connected to the slider inputs. Use it when the control has a readable label on screen.       |
| `SliderValue` | Optional formatted value display. It receives formatted values and raw numeric values as render arguments.  |
| `SliderThumb` | Interactive handle. Render one thumb for a single value and multiple indexed thumbs for a range.            |

The control, track, and indicator are service-like slots for this component. They stay internal by
default and can be styled through `classNames.control`, `classNames.track`, and
`classNames.indicator` when token-level customization is not enough.

## Composition [#composition]

Use `Slider` for behavior props such as `value`, `defaultValue`, `onValueChange`,
`onValueCommitted`, `min`, `max`, `step`, `largeStep`, `minStepsBetweenValues`, `orientation`,
`thumbAlignment`, `thumbCollisionBehavior`, `name`, and `form`.

Use `SliderThumb` for the accessible name of each handle. A visible `SliderLabel` labels the group,
but multi-thumb sliders still need distinct `aria-label` values on each thumb.

Style the root with `className`, visible parts with their own `className`, and internally rendered
track structure with `classNames`:

```tsx
<Slider
  defaultValue={56}
  className={styles.slider}
  classNames={{
    control: styles.control,
    track: styles.track,
    indicator: styles.indicator,
  }}
>
  <SliderLabel>Temperature</SliderLabel>
  <SliderValue>{([formattedValue]) => `${formattedValue}%`}</SliderValue>
  <SliderThumb aria-label="Temperature" className={styles.thumb} />
</Slider>
```

## Examples [#examples]

### Range [#range]

Pass an array value and render one `SliderThumb` for each value to let users select a range.

<Preview>
  <RangeSliderExample />

  <Preview.Code>
    {`
          import {
            Slider,
            SliderLabel,
            SliderValue,
            SliderThumb,
          } from "moduix";
          import { useState } from "react";

          export function RangeSliderDemo() {
            const [value, setValue] = useState([20, 70] as readonly number[]);

            return (
              <Slider value={value} min={0} max={100} onValueChange={setValue}>
                <SliderLabel>Price range</SliderLabel>
                <SliderValue>{([minValue, maxValue]) => \`\${minValue} - \${maxValue}\`}</SliderValue>
                <SliderThumb index={0} aria-label="Minimum price" />
                <SliderThumb index={1} aria-label="Maximum price" />
              </Slider>
            );
          }
        `}
  </Preview.Code>
</Preview>

### Steps and Constraints [#steps-and-constraints]

Use `step`, `largeStep`, and `minStepsBetweenValues` to make range selection snap to meaningful
increments. `thumbCollisionBehavior` controls what happens when thumbs meet.

<Preview>
  <SteppedRangeSliderExample />

  <Preview.Code>
    {`
          import {
            Slider,
            SliderLabel,
            SliderValue,
            SliderThumb,
          } from "moduix";
          import { useState } from "react";

          const priceFormatter = new Intl.NumberFormat("en-US", {
            style: "currency",
            currency: "USD",
            maximumFractionDigits: 0,
          });

          export function SteppedRangeSliderDemo() {
            const [value, setValue] = useState([250, 750] as readonly number[]);

            return (
              <Slider
                value={value}
                min={0}
                max={1000}
                step={50}
                largeStep={200}
                minStepsBetweenValues={2}
                thumbCollisionBehavior="none"
                format={{
                  style: "currency",
                  currency: "USD",
                  maximumFractionDigits: 0,
                }}
                onValueChange={setValue}
              >
                <SliderLabel>Budget</SliderLabel>
                <SliderValue>
                  {(_, [minValue, maxValue]) =>
                    \`\${priceFormatter.format(minValue)} - \${priceFormatter.format(maxValue)}\`
                  }
                </SliderValue>
                <SliderThumb index={0} aria-label="Minimum budget" />
                <SliderThumb index={1} aria-label="Maximum budget" />
              </Slider>
            );
          }
        `}
  </Preview.Code>
</Preview>

### Controlled [#controlled]

Control the value from React state when the slider needs to synchronize with another control.

<Preview>
  <ControlledSliderExample />

  <Preview.Code>
    {`
          import {
            Slider,
            SliderLabel,
            SliderValue,
            SliderThumb,
          } from "moduix";
          import { useState } from "react";
          import styles from "./slider.module.css";

          export function ControlledSliderDemo() {
            const [value, setValue] = useState(24);

            return (
              <div className={styles.stack}>
                <Slider value={value} onValueChange={setValue}>
                  <SliderLabel>Brightness</SliderLabel>
                  <SliderValue>{([formattedValue]) => \`\${formattedValue}%\`}</SliderValue>
                  <SliderThumb aria-label="Brightness" />
                </Slider>
                <input
                  className={styles.range}
                  type="range"
                  min={0}
                  max={100}
                  value={value}
                  onChange={(event) => {
                    setValue(Number(event.target.value));
                  }}
                />
              </div>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .stack {
            display: grid;
            gap: var(--spacing-4);
          }

          .range {
            width: 12rem;
          }
        `}
  </Preview.CSS>
</Preview>

### Vertical [#vertical]

Set `orientation="vertical"` and size the vertical track with CSS custom properties.

<Preview>
  <VerticalSliderExample />

  <Preview.Code>
    {`
          import {
            Slider,
            SliderLabel,
            SliderValue,
            SliderThumb,
          } from "moduix";
          import styles from "./slider.module.css";

          export function VerticalSliderDemo() {
            return (
              <div className={styles.verticalContainer}>
                <Slider orientation="vertical" defaultValue={60} className={styles.verticalSlider}>
                  <SliderLabel>Output</SliderLabel>
                  <SliderValue>{([formattedValue]) => \`\${formattedValue}%\`}</SliderValue>
                  <SliderThumb aria-label="Output" />
                </Slider>
              </div>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .verticalContainer {
            display: flex;
            height: 14rem;
          }

          .verticalSlider {
            --slider-width-vertical: auto;
            --slider-height: 12rem;
          }
        `}
  </Preview.CSS>
</Preview>

### Edge Thumb Alignment [#edge-thumb-alignment]

Use `thumbAlignment="edge"` when the thumb should stay inside the control at the minimum and maximum values.

<Preview>
  <EdgeThumbAlignmentSliderExample />

  <Preview.Code>
    {`
          import {
            Slider,
            SliderLabel,
            SliderValue,
            SliderThumb,
          } from "moduix";

          export function EdgeThumbAlignmentSliderDemo() {
            return (
              <Slider defaultValue={0} thumbAlignment="edge">
                <SliderLabel>Zoom</SliderLabel>
                <SliderValue>{([formattedValue]) => \`\${formattedValue}%\`}</SliderValue>
                <SliderThumb aria-label="Zoom" />
              </Slider>
            );
          }
        `}
  </Preview.Code>
</Preview>

### Disabled [#disabled]

Use `disabled` to make the whole slider non-interactive while preserving its current value.

<Preview>
  <DisabledSliderExample />

  <Preview.Code>
    {`
          import {
            Slider,
            SliderLabel,
            SliderValue,
            SliderThumb,
          } from "moduix";

          export function DisabledSliderDemo() {
            return (
              <Slider defaultValue={32} disabled>
                <SliderLabel>Notifications</SliderLabel>
                <SliderValue>{([formattedValue]) => \`\${formattedValue}%\`}</SliderValue>
                <SliderThumb aria-label="Notifications" />
              </Slider>
            );
          }
        `}
  </Preview.Code>
</Preview>

### Custom Styles [#custom-styles]

Use `className` for the root and `classNames` for internally rendered slots.

<Preview>
  <CustomClassesSliderExample />

  <Preview.Code>
    {`
          import {
            Slider,
            SliderLabel,
            SliderValue,
            SliderThumb,
          } from "moduix";
          import styles from "./slider.module.css";

          export function CustomClassesSliderDemo() {
            return (
              <Slider
                defaultValue={56}
                classNames={{
                  control: styles.customControl,
                  track: styles.customTrack,
                  indicator: styles.customIndicator,
                }}
              >
                <SliderLabel>Temperature</SliderLabel>
                <SliderValue>{([formattedValue]) => \`\${formattedValue}%\`}</SliderValue>
                <SliderThumb aria-label="Temperature" className={styles.customThumb} />
              </Slider>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .customControl {
            --slider-control-padding-y: var(--spacing-3);
          }

          .customTrack {
            --slider-track-size: 0.625rem;
            --slider-track-bg: color-mix(in oklab, var(--color-chart-4) 18%, var(--color-muted));
            --slider-track-border-color: transparent;
          }

          .customIndicator {
            --slider-indicator-bg: var(--color-chart-4);
          }

          .customThumb {
            --slider-thumb-size: 1.25rem;
            --slider-thumb-bg: var(--color-chart-4);
            --slider-thumb-border-color: var(--color-background);
          }
        `}
  </Preview.CSS>
</Preview>
