# Slider

**An input for selecting a value, or a range of values, from a given range.**

**Reference:** [View Source](https://github.com/jramke/fluid-primitives/tree/main/Resources/Private/Primitives/Slider) · [Zag.js Docs](https://zagjs.com/components/react/slider)

**Slider.html**

```html
<ui:slider.root defaultValue="30">
    <div class="flex justify-between">
        <ui:slider.label>Volume</ui:slider.label>
        <ui:slider.valueText />
    </div>
    <ui:slider.control>
        <ui:slider.track>
            <ui:slider.range />
        </ui:slider.track>
        <ui:slider.thumb />
    </ui:slider.control>
</ui:slider.root>
```

**Slider.ts**

```ts
import { mountAll } from 'fluid-primitives';
import { Slider } from 'fluid-primitives/slider';

mountAll('ui:slider', ({ props }) => {
    const slider = new Slider(props);
    slider.init();
    return slider;
});

```

## Features

- Full keyboard navigation support with arrow, page and home/end keys
- Supports touch, mouse and pointer interactions, including clicking the track
- Supports a range of values with multiple thumbs, with a configurable minimum gap between them
- Supports custom step, `largeStep` and min/max values
- Supports marks/ticks along the track
- Accepts `defaultValue` as a single number for one thumb, or a list of numbers for a range
- Works with the Field component for form integration

## Installation

```bash
typo3 ui:add slider
```

Or copy the files manually from GitHub (https://github.com/jramke/fluid-primitives.com/tree/main/packages/docs/Resources/Private/Registry/Slider) into your project.

Read more about installing [Components and Primitives](/docs/core-concepts/primitives.md).

## Examples

### Range

Use two thumbs to let users pick a range of values.

**Slider.html**

```html
<ui:slider.root defaultValue="{0: 25, 1: 75}" minStepsBetweenThumbs="10">
    <div class="flex justify-between">
        <ui:slider.label>Price range</ui:slider.label>
        <ui:slider.valueText />
    </div>
    <ui:slider.control>
        <ui:slider.track>
            <ui:slider.range />
        </ui:slider.track>
        <ui:slider.thumb index="0" />
        <ui:slider.thumb index="1" />
    </ui:slider.control>
</ui:slider.root>
```

### With marks

Render `slider.marker` elements to show ticks - with a label - along the track.

**Slider.html**

```html
<ui:slider.root defaultValue="{0: 40}">
    <div class="flex justify-between">
        <ui:slider.label>Zoom level</ui:slider.label>
        <ui:slider.valueText />
    </div>
    <ui:slider.control>
        <ui:slider.track>
            <ui:slider.range />
        </ui:slider.track>
        <ui:slider.thumb />
    </ui:slider.control>
    <ui:slider.markerGroup>
        <ui:slider.marker value="0">0%</ui:slider.marker>
        <ui:slider.marker value="25">25%</ui:slider.marker>
        <ui:slider.marker value="50">50%</ui:slider.marker>
        <ui:slider.marker value="75">75%</ui:slider.marker>
        <ui:slider.marker value="100">100%</ui:slider.marker>
    </ui:slider.markerGroup>
</ui:slider.root>
```

### Disabled

Prevent the slider from being interacted with.

**Slider.html**

```html
<ui:slider.root defaultValue="{0: 50}" disabled="{true}">
    <div class="flex justify-between">
        <ui:slider.label>Brightness</ui:slider.label>
        <ui:slider.valueText />
    </div>
    <ui:slider.control>
        <ui:slider.track>
            <ui:slider.range />
        </ui:slider.track>
        <ui:slider.thumb />
    </ui:slider.control>
</ui:slider.root>
```

### With a number input

Pair the slider with a `number-input` for precise entry, keeping both in sync by updating one whenever the other changes. Both are mounted as independent, hydration-controlled instances (`controlled="{true}"` + a fixed `rootId`) so a custom entry file can wire them together, the same pattern [Combobox's custom filter example](/docs/components/combobox.md#custom-filter-api) uses. Both components' own `onValueChange` call into one shared `setValue()` function instead of writing to each other directly, so there's a single guard - skip if it's already the current value - rather than a `syncing` flag guessing which call is the "real" one.

**Slider.html**

```html
<f:variable name="defaultValue" value="50" />

<div class="flex flex-col items-center gap-4">
    <ui:slider.root rootId="synced-slider" controlled="{true}" defaultValue="{defaultValue}" class="max-w-55">
        <ui:slider.label>Discount</ui:slider.label>
        <ui:slider.control>
            <ui:slider.track>
                <ui:slider.range />
            </ui:slider.track>
            <ui:slider.thumb />
        </ui:slider.control>
    </ui:slider.root>

    <ui:numberInput.root rootId="synced-number-input" controlled="{true}" defaultValue="{defaultValue}" min="0" max="100" class="shrink-0">
        <ui:numberInput.control>
            <ui:numberInput.decrementTrigger />
            <ui:numberInput.input />
            <ui:numberInput.incrementTrigger />
        </ui:numberInput.control>
    </ui:numberInput.root>
</div>

<vite:asset entry="EXT:docs/Resources/Private/Components/ui/Slider/Examples/WithNumberInput.entry.ts" />
```

**WithNumberInput.entry.ts**

```ts
import { getHydrationData } from 'fluid-primitives';
import { NumberInput } from 'fluid-primitives/number-input';
import { Slider } from 'fluid-primitives/slider';

(() => {
    const sliderData = getHydrationData('ui:slider', 'synced-slider');
    const numberInputData = getHydrationData('ui:numberInput', 'synced-number-input');

    if (!sliderData || !numberInputData) {
        console.error('Missing hydration data for slider + number input sync example');
        return;
    }

    let currentValue = numberInputData.props.defaultValue;

    function setValue(next: number) {
        if (next === currentValue) return;
        currentValue = next;
        slider.api.setValue([next]);
        numberInput.api.setValue(next);
    }

    const slider = new Slider({
        ...sliderData.props,
        onValueChange: ({ value }) => setValue(value[0]),
    });

    const numberInput = new NumberInput({
        ...numberInputData.props,
        onValueChange: ({ valueAsNumber }) => {
            if (!Number.isNaN(valueAsNumber)) setValue(valueAsNumber);
        },
    });

    slider.init();
    numberInput.init();
})();

```

### With a Field

Wrap the slider in [`ui:field.root`](/docs/components/field.md) to get label association and `name`/`disabled`/`invalid` state propagated to the thumb automatically - a donation amount picker, with marks for common preset amounts.

**Slider.html**

```html
<ui:field.root name="donationAmount" class="max-w-xs">
    <ui:slider.root defaultValue="25" min="0" max="200" step="5">
        <div class="flex items-center justify-between">
            <ui:slider.label>Donation amount</ui:slider.label>
            <span class="text-sm text-muted-foreground">
                $
                <ui:slider.valueText />
            </span>
        </div>
        <ui:slider.control>
            <ui:slider.track>
                <ui:slider.range />
            </ui:slider.track>
            <ui:slider.thumb />
        </ui:slider.control>
        <ui:slider.markerGroup>
            <ui:slider.marker value="10">$10</ui:slider.marker>
            <ui:slider.marker value="25">$25</ui:slider.marker>
            <ui:slider.marker value="50">$50</ui:slider.marker>
            <ui:slider.marker value="100">$100</ui:slider.marker>
        </ui:slider.markerGroup>
    </ui:slider.root>
    <ui:field.description>Choose an amount to support this event - every contribution helps.</ui:field.description>
</ui:field.root>
```

### With a dragging indicator

Nest `slider.draggingIndicator` inside a `slider.thumb` to show its current value in a small tooltip while it's being dragged - it's hidden the rest of the time.

**Slider.html**

```html
<ui:slider.root defaultValue="{0: 50}">
    <div class="flex justify-between">
        <ui:slider.label>Progress</ui:slider.label>
        <ui:slider.valueText />
    </div>
    <ui:slider.control>
        <ui:slider.track>
            <ui:slider.range />
        </ui:slider.track>
        <ui:slider.thumb>
            <ui:slider.draggingIndicator />
        </ui:slider.thumb>
    </ui:slider.control>
</ui:slider.root>
```

### With decimal values

Set `step` to a fraction (e.g. `0.01`) to get fine-grained, decimal precision instead of whole numbers - useful whenever the value represents something more precise than an integer count.

**Slider.html**

```html
<ui:slider.root defaultValue="7.98" min="5" max="10" step="0.01">
    <div class="flex justify-between">
        <ui:slider.label>Precision level</ui:slider.label>
        <ui:slider.valueText />
    </div>
    <ui:slider.control>
        <ui:slider.track>
            <ui:slider.range />
        </ui:slider.track>
        <ui:slider.thumb />
    </ui:slider.control>
</ui:slider.root>

```

## API Reference

### slider.root

Contains every part of the slider. Renders a `<div>` element.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `disabled` | `boolean` | No | `-` | Whether the slider is disabled. |
| `readOnly` | `boolean` | No | `-` | Whether the slider is read-only. |
| `invalid` | `boolean` | No | `-` | Whether the slider is invalid. |
| `name` | `string` | No | `-` | The name associated with each slider thumb (when used in a form). |
| `form` | `string` | No | `-` | The associate form of the underlying hidden input elements. |
| `defaultValue` | `float\|integer\|array` | No | `-` | The initial value of the slider when rendered - a single number for one thumb, or a list of numbers (one per thumb) for a range. Use when you don't need to control the value of the slider. |
| `min` | `float` | No | `0` | The minimum value of the slider. |
| `max` | `float` | No | `100` | The maximum value of the slider. |
| `step` | `float` | No | `1` | The step value of the slider. |
| `largeStep` | `float` | No | `-` | The step value of the slider when the `Shift` key is held, or the `PageUp`/`PageDown` keys are used. Defaults to `10 * step`. |
| `minStepsBetweenThumbs` | `float` | No | `0` | The minimum permitted steps between multiple thumbs. `minStepsBetweenThumbs * step` reflects the gap between the thumbs. |
| `orientation` | `Enum\Orientation` | No | `Horizontal` | The orientation of the slider. |
| `origin` | `Enum\SliderOrigin` | No | `Start` | The origin of the slider range. The track is filled from the origin to the thumb for single values. `start` is useful when the value represents an absolute value, `center` when it represents an offset (relative), and `end` when it represents an offset from the end. |
| `thumbAlignment` | `Enum\SliderThumbAlignment` | No | `Contain` | The alignment of the slider thumb relative to the track. `center` lets the thumb extend beyond the bounds of the track, `contain` keeps it within the track's bounds. |
| `thumbSize` | `array` | No | `-` | The slider thumb dimensions, with a `width` and `height` key. |
| `thumbCollisionBehavior` | `Enum\SliderThumbCollisionBehavior` | No | `None` | Controls how thumbs behave when they collide during pointer interactions. `none` keeps thumbs from moving past each other, `push` makes them push each other, `swap` makes them swap places. |
| `asChild` | `boolean` | No | `-` | If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. |
| `rootId` | `string` | No | `-` | The root ID of the component, used for hydration and identification. |
| `ids` | `array` | No | `[]` | The IDs of of the component parts for composition. |
| `controlled` | `boolean` | No | `false` | If true, the component is meant to be initialized manually inside another component |
| `class` | `string` | No | `-` | The CSS class(es) to be applied to the component. |
| `attributes` | `array` | No | `[]` | Additional attributes that should be rendered on the component where ui:attributes is used. |

#### Rendered data attributes

| Attribute | Description |
| --- | --- |
| `data-scope` | slider |
| `data-part` | root |
| `data-disabled` | Present when disabled |
| `data-orientation` | The orientation of the slider |
| `data-dragging` | Present when in the dragging state |
| `data-invalid` | Present when invalid |
| `data-focus` | Present when focused |

### slider.label

Renders an accessible label for the slider. Renders a `<label>` element.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `asChild` | `boolean` | No | `-` | If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. |
| `class` | `string` | No | `-` | The CSS class(es) to be applied to the component. |
| `attributes` | `array` | No | `[]` | Additional attributes that should be rendered on the component where ui:attributes is used. |

#### Rendered data attributes

| Attribute | Description |
| --- | --- |
| `data-scope` | slider |
| `data-part` | label |
| `data-disabled` | Present when disabled |
| `data-orientation` | The orientation of the label |
| `data-invalid` | Present when invalid |
| `data-dragging` | Present when in the dragging state |
| `data-focus` | Present when focused |

### slider.valueText

Displays a textual representation of the current value. Renders a `<span>` element.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `asChild` | `boolean` | No | `-` | If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. |
| `class` | `string` | No | `-` | The CSS class(es) to be applied to the component. |
| `attributes` | `array` | No | `[]` | Additional attributes that should be rendered on the component where ui:attributes is used. |

#### Rendered data attributes

| Attribute | Description |
| --- | --- |
| `data-scope` | slider |
| `data-part` | value-text |
| `data-disabled` | Present when disabled |
| `data-orientation` | The orientation of the valuetext |
| `data-invalid` | Present when invalid |
| `data-focus` | Present when focused |

### slider.control

Wraps the track and thumbs, and handles pointer interaction with the track. Renders a `<div>` element.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `asChild` | `boolean` | No | `-` | If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. |
| `class` | `string` | No | `-` | The CSS class(es) to be applied to the component. |
| `attributes` | `array` | No | `[]` | Additional attributes that should be rendered on the component where ui:attributes is used. |

#### Rendered data attributes

| Attribute | Description |
| --- | --- |
| `data-scope` | slider |
| `data-part` | control |
| `data-dragging` | Present when in the dragging state |
| `data-disabled` | Present when disabled |
| `data-orientation` | The orientation of the control |
| `data-invalid` | Present when invalid |
| `data-focus` | Present when focused |

### slider.track

Renders the full length of the slider's track. Renders a `<div>` element.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `asChild` | `boolean` | No | `-` | If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. |
| `class` | `string` | No | `-` | The CSS class(es) to be applied to the component. |
| `attributes` | `array` | No | `[]` | Additional attributes that should be rendered on the component where ui:attributes is used. |

#### Rendered data attributes

| Attribute | Description |
| --- | --- |
| `data-scope` | slider |
| `data-part` | track |
| `data-disabled` | Present when disabled |
| `data-invalid` | Present when invalid |
| `data-dragging` | Present when in the dragging state |
| `data-orientation` | The orientation of the track |
| `data-focus` | Present when focused |

### slider.range

Renders the filled portion of the track between the origin and the thumb(s). Renders a `<div>` element.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `asChild` | `boolean` | No | `-` | If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. |
| `class` | `string` | No | `-` | The CSS class(es) to be applied to the component. |
| `attributes` | `array` | No | `[]` | Additional attributes that should be rendered on the component where ui:attributes is used. |

#### Rendered data attributes

| Attribute | Description |
| --- | --- |
| `data-scope` | slider |
| `data-part` | range |
| `data-dragging` | Present when in the dragging state |
| `data-focus` | Present when focused |
| `data-invalid` | Present when invalid |
| `data-disabled` | Present when disabled |
| `data-orientation` | The orientation of the range |

### slider.thumb

Renders a draggable handle for one value. Takes an `index` prop to identify which value it controls. Renders a `<div>` element.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `index` | `integer` | No | `0` | The index of the thumb. Use a distinct index per `slider.thumb` for a range slider with multiple thumbs. |
| `name` | `string` | No | `-` | The name of the thumb. Defaults to the root `name`. |
| `asChild` | `boolean` | No | `-` | If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. |
| `class` | `string` | No | `-` | The CSS class(es) to be applied to the component. |
| `attributes` | `array` | No | `[]` | Additional attributes that should be rendered on the component where ui:attributes is used. |

#### Rendered data attributes

| Attribute | Description |
| --- | --- |
| `data-scope` | slider |
| `data-part` | thumb |
| `data-index` | The index of the item |
| `data-name` |  |
| `data-disabled` | Present when disabled |
| `data-orientation` | The orientation of the thumb |
| `data-focus` | Present when focused |
| `data-dragging` | Present when in the dragging state |

### slider.hiddenInput

Provides a native input for form submission, nested inside a `slider.thumb`. Renders an `<input>` element.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `asChild` | `boolean` | No | `-` | If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. |
| `class` | `string` | No | `-` | The CSS class(es) to be applied to the component. |
| `attributes` | `array` | No | `[]` | Additional attributes that should be rendered on the component where ui:attributes is used. |

### slider.markerGroup

Groups the marks/ticks rendered along the track. Renders a `<div>` element.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `asChild` | `boolean` | No | `-` | If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. |
| `class` | `string` | No | `-` | The CSS class(es) to be applied to the component. |
| `attributes` | `array` | No | `[]` | Additional attributes that should be rendered on the component where ui:attributes is used. |

#### Rendered data attributes

| Attribute | Description |
| --- | --- |
| `data-scope` | slider |
| `data-part` | marker-group |
| `data-orientation` | The orientation of the markergroup |

### slider.marker

Renders a single mark/tick at a given value. Renders a `<div>` element.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `value` | `float` | Yes | `-` | The value along the track at which to render the marker. |
| `asChild` | `boolean` | No | `-` | If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. |
| `class` | `string` | No | `-` | The CSS class(es) to be applied to the component. |
| `attributes` | `array` | No | `[]` | Additional attributes that should be rendered on the component where ui:attributes is used. |

#### Rendered data attributes

| Attribute | Description |
| --- | --- |
| `data-scope` | slider |
| `data-part` | marker |
| `data-orientation` | The orientation of the marker |
| `data-value` | The value of the item |
| `data-disabled` | Present when disabled |
| `data-state` |  |

### slider.draggingIndicator

Displays the current value while its thumb is being dragged. Nested inside a `slider.thumb`, whose index it follows automatically. Renders a `<div>` element.

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `asChild` | `boolean` | No | `-` | If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. |
| `class` | `string` | No | `-` | The CSS class(es) to be applied to the component. |
| `attributes` | `array` | No | `[]` | Additional attributes that should be rendered on the component where ui:attributes is used. |

#### Rendered data attributes

| Attribute | Description |
| --- | --- |
| `data-scope` | slider |
| `data-part` | dragging-indicator |
| `data-orientation` | The orientation of the draggingindicator |
| `data-state` | "open" \| "closed" |

### Machine JavaScript API

| Name | Type | Description |
| --- | --- | --- |
| `value` | `number[]` | The value of the slider. |
| `dragging` | `boolean` | Whether the slider is being dragged. |
| `focused` | `boolean` | Whether the slider is focused. |
| `setValue` | `(value: number[]) => void` | Function to set the value of the slider. |
| `getThumbValue` | `(index: number) => number` | Returns the value of the thumb at the given index. |
| `setThumbValue` | `(index: number, value: number) => void` | Sets the value of the thumb at the given index. |
| `getValuePercent` | `(value: number) => number` | Returns the percent of the thumb at the given index. |
| `getPercentValue` | `(percent: number) => number` | Returns the value of the thumb at the given percent. |
| `getThumbPercent` | `(index: number) => number` | Returns the percent of the thumb at the given index. |
| `setThumbPercent` | `(index: number, percent: number) => void` | Sets the percent of the thumb at the given index. |
| `getThumbMin` | `(index: number) => number` | Returns the min value of the thumb at the given index. |
| `getThumbMax` | `(index: number) => number` | Returns the max value of the thumb at the given index. |
| `increment` | `(index: number) => void` | Function to increment the value of the slider at the given index. |
| `decrement` | `(index: number) => void` | Function to decrement the value of the slider at the given index. |
| `focus` | `VoidFunction` | Function to focus the slider. This focuses the first thumb. |

### Accessibility

| Key | Description |
| --- | --- |
| `ArrowRight`  | <span>Increments the slider based on defined step</span> |
| `ArrowLeft`  | <span>Decrements the slider based on defined step</span> |
| `ArrowUp`  | <span>Increases the value by the step amount.</span> |
| `ArrowDown`  | <span>Decreases the value by the step amount.</span> |
| `PageUp`  | <span>Increases the value by the <code>largeStep</code> amount.</span> |
| `PageDown`  | <span>Decreases the value by the <code>largeStep</code> amount.</span> |
| `Shift + ArrowUp`  | <span>Increases the value by the <code>largeStep</code> amount.</span> |
| `Shift + ArrowDown`  | <span>Decreases the value by the <code>largeStep</code> amount.</span> |
| `Home`  | Sets the value to its minimum. |
| `End`  | Sets the value to its maximum. |

## Anatomy

```html
<primitives:slider.root>
    <primitives:slider.label />
    <primitives:slider.valueText />
    <primitives:slider.control>
        <primitives:slider.track>
            <primitives:slider.range />
        </primitives:slider.track>
        <primitives:slider.thumb index="0">
            <primitives:slider.hiddenInput />
        </primitives:slider.thumb>
        <primitives:slider.markerGroup>
            <primitives:slider.marker value="25" />
        </primitives:slider.markerGroup>
    </primitives:slider.control>
</primitives:slider.root>
```
