> ## Documentation Index
> Fetch the complete documentation index at: https://docs.twenty.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Slider

> Choose a numeric value or range with draggable, keyboard-accessible thumbs.

export const StoryEmbed = ({storyId, title, height = 120}) => <>
    <Tabs>
      <Tab title="Light">
        <iframe title={`${title} (light)`} src={`https://storybook.twenty.com/iframe.html?id=${storyId}&viewMode=story&globals=colorScheme:light`} width="100%" height={height} loading="lazy" allow="clipboard-write" style={{
  border: 0
}} />
      </Tab>
      <Tab title="Dark">
        <iframe title={`${title} (dark)`} src={`https://storybook.twenty.com/iframe.html?id=${storyId}&viewMode=story&globals=colorScheme:dark`} width="100%" height={height} loading="lazy" allow="clipboard-write" style={{
  border: 0
}} />
      </Tab>
    </Tabs>
    <a href={`https://storybook.twenty.com/?path=/story/${storyId}`}>
      Open in Storybook
    </a>
  </>;

`Slider` supports a single number or an array of numbers for a range. Compose its label, track, indicator, and thumbs inside `Root`.

<StoryEmbed storyId="ui-input-slider--display" title="Slider example" height={120} />

## Usage

### Uncontrolled state

Use `defaultValue` to set the initial volume. The slider owns subsequent changes.

```tsx theme={null}
import { Slider } from 'twenty-ui/primitives/input';
import { Text } from 'twenty-ui/primitives/typography';

export const VolumeSlider = () => (
  <Slider.Root name="volume" defaultValue={40} min={0} max={100} step={1}>
    <Text
      render={<div />}
      style={{ display: 'flex', justifyContent: 'space-between' }}
    >
      <Slider.Label>Volume</Slider.Label>
      <Slider.Value />
    </Text>
    <Slider.Control>
      <Slider.Track>
        <Slider.Indicator />
        <Slider.Thumb />
      </Slider.Track>
    </Slider.Control>
  </Slider.Root>
);
```

## Visual choices

Compare the accent and success colors at minimum, middle, maximum, and disabled values. The enabled sliders remain interactive.

<StoryEmbed storyId="ui-input-slider--catalog" title="Visual choices for slider" height={340} />

## Anatomy

Put the track and thumbs inside `Slider.Control`, which handles pointer interaction. This tree shows a single-thumb slider.

```text theme={null}
Slider.Root
├── Slider.Label
├── Slider.Value
└── Slider.Control
    └── Slider.Track
        ├── Slider.Indicator
        └── Slider.Thumb
```

| Part | Requirement | Purpose |
| - | - | - |
| `Root` | Required | Owns the value, bounds, step, and orientation. |
| `Control` | Required | Handles pointer interaction and positions the thumbs. |
| `Track` | Required for the styled track | Shows the full value range. |
| `Thumb` | One per adjustable value | Provides a focusable handle with keyboard interaction. |
| `Label` | Recommended | Supplies a visible label; every thumb needs an accessible name. |
| `Value` | Optional | Displays the current numeric value. |
| `Indicator` | Optional | Shows the selected portion of the track. |

For a range, add a thumb for each value with its matching `index` and a distinct accessible name. Labels and numeric output stay outside `Control`, so they do not become part of the draggable area.

## Controlled state

Pass `value` and update it with `onValueChange` to own the same volume in React state. The callback receives the next value and event details.

```tsx theme={null}
import { useState } from 'react';
import { Slider } from 'twenty-ui/primitives/input';

export const VolumeSlider = () => {
  const [volume, setVolume] = useState(40);

  return (
    <Slider.Root
      name="volume"
      value={volume}
      onValueChange={setVolume}
      min={0}
      max={100}
      step={1}
    >
      <Slider.Label>Volume</Slider.Label>
      <Slider.Value />
      <Slider.Control>
        <Slider.Track>
          <Slider.Indicator />
          <Slider.Thumb />
        </Slider.Track>
      </Slider.Control>
    </Slider.Root>
  );
};
```

Use `onValueChange` to keep the controlled value current during an interaction. `onValueCommitted` handles the final value when the interaction finishes, for example to save it. Both callbacks are also available in uncontrolled mode without taking ownership of the value.

Change details include the originating `event`, the interaction `reason`, the `activeThumbIndex`, and `cancel()`. Commit details use Base UI's generic event contract and do not include change cancellation. A canceled change does not commit.

## Range

```tsx theme={null}
import { Slider } from 'twenty-ui/primitives/input';

export const PriceRange = () => (
  <Slider.Root
    defaultValue={[25, 75]}
    min={0}
    max={100}
    minStepsBetweenValues={10}
  >
    <Slider.Label>Price range</Slider.Label>
    <Slider.Value />
    <Slider.Control>
      <Slider.Track>
        <Slider.Indicator />
        <Slider.Thumb index={0} aria-label="Minimum price" />
        <Slider.Thumb index={1} aria-label="Maximum price" />
      </Slider.Track>
    </Slider.Control>
  </Slider.Root>
);
```

Give range thumbs distinct accessible names and an `index` corresponding to their value. `minStepsBetweenValues` sets the minimum gap in steps. The root's generic value type is a number or a readonly array of numbers.

To control a range, store the array in React state and pass it through `value` and `onValueChange`, just as for a single value.

## Keyboard and appearance

Arrow keys adjust a focused thumb by a step. Home and End move it toward the bounds while respecting range constraints. `orientation="vertical"` changes the layout and keyboard direction. Use `disabled` on `Root` to prevent changes.

Set `color="success"` for the alternative palette; the default is `accent`.

The default `thumbAlignment` is `edge`. Pass `center` or `edge-client-only` to override it. Native `className`, `style`, and `render` composition remain available on each part, including state functions.

<Note>
  With `edge` and `edge-client-only` alignment, Base UI 1.8 and 1.9 never
  re-measure a thumb after its `Slider.Control` resizes, and a slider mounted
  while hidden keeps its thumbs hidden once shown
  ([mui/base-ui#5995](https://github.com/mui/base-ui/issues/5995)). React front
  components hit the same issue on their first render. Use
  `thumbAlignment="center"` until a Base UI release includes the fix
  ([mui/base-ui#6000](https://github.com/mui/base-ui/pull/6000)).
</Note>

## Native integration and types

`Slider.Root` infers a number or a readonly numeric array from `value` or `defaultValue`, retaining that type in both callbacks. `SliderRootProps` accepts both by default; use `SliderRootProps<number>` for single-value helpers or `SliderRootProps<readonly number[]>` for range helpers.

Every part has an exported props type in `twenty-ui/primitives/input`: `SliderRootProps`, `SliderControlProps`, `SliderTrackProps`, `SliderIndicatorProps`, `SliderThumbProps`, `SliderLabelProps`, and `SliderValueProps`.

Refs target div elements for Root, Control, Track, Indicator, Thumb, and Label, and an output element for Value. A Thumb's ref targets its visible handle; its accessible name and native form value belong to the nested range input. Use `name` and `form` on Root for native submission, with one input per range value.

## Props

The reference is generated from the public component types. Native attributes, including accessible names and event handlers, are also accepted on parts that render elements.

### Slider.Root

<ParamField body="Root.className" type="string | ((state: SliderRootState) => string | undefined)">
  CSS class applied to the element, or a function that
  returns a class based on the component's state.
</ParamField>

<ParamField body="Root.color" type="&#x22;accent&#x22; | &#x22;success&#x22;" default="accent">
  Color of the indicator and thumbs.
</ParamField>

<ParamField body="Root.defaultValue" type="number | readonly number[]">
  The uncontrolled value of the slider when it's initially rendered.

  To render a controlled slider, use the `value` prop instead.
</ParamField>

<ParamField body="Root.disabled" type="boolean" default="false">
  Whether the slider should ignore user interaction.
</ParamField>

<ParamField body="Root.form" type="string">
  Identifies the form that owns the slider inputs.
  Useful when the slider is rendered outside the form.
</ParamField>

<ParamField body="Root.format" type="NumberFormatOptions">
  Options to format the value.
</ParamField>

<ParamField body="Root.largeStep" type="number" default="10">
  The granularity with which the slider can step through values when using Page Up/Page Down or Shift + Arrow Up/Arrow Down.
</ParamField>

<ParamField body="Root.locale" type="LocalesArgument">
  The locale used by `Intl.NumberFormat` when formatting the value.
  Defaults to the user's runtime locale.
</ParamField>

<ParamField body="Root.max" type="number" default="100">
  The maximum allowed value of the slider.
  Should not be equal to min.
</ParamField>

<ParamField body="Root.min" type="number" default="0">
  The minimum allowed value of the slider.
  Should not be equal to max.
</ParamField>

<ParamField body="Root.minStepsBetweenValues" type="number" default="0">
  The minimum steps between values in a range slider.
</ParamField>

<ParamField body="Root.name" type="string">
  Identifies the field when a form is submitted.
</ParamField>

<ParamField body="Root.onValueChange" type="((value: TValue extends number ? number : TValue, eventDetails: SliderRootChangeEventDetails) => void)">
  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
</ParamField>

<ParamField body="Root.onValueCommitted" type="((value: TValue extends number ? number : TValue, eventDetails: SliderRootCommitEventDetails) => void)">
  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
</ParamField>

<ParamField body="Root.orientation" type="&#x22;horizontal&#x22; | &#x22;vertical&#x22;" default="horizontal">
  The component orientation.
</ParamField>

<ParamField body="Root.render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SliderRootState>">
  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.
</ParamField>

<ParamField body="Root.step" type="number" default="1">
  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.
</ParamField>

<ParamField body="Root.style" type="CSSProperties | ((state: SliderRootState) => CSSProperties | undefined)">
  Style applied to the element, or a function that
  returns a style object based on the component's state.
</ParamField>

<ParamField body="Root.thumbAlignment" type="&#x22;center&#x22; | &#x22;edge-client-only&#x22; | &#x22;edge&#x22;" default="edge">
  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
</ParamField>

<ParamField body="Root.thumbCollisionBehavior" type="&#x22;none&#x22; | &#x22;push&#x22; | &#x22;swap&#x22;" default="push">
  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.
</ParamField>

<ParamField body="Root.value" type="number | readonly number[]">
  The value of the slider.
  For range sliders, provide an array with one value per thumb.
</ParamField>

### Slider.Control

<ParamField body="Control.className" type="string | ((state: SliderControlState) => string | undefined)">
  CSS class applied to the element, or a function that
  returns a class based on the component's state.
</ParamField>

<ParamField body="Control.render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SliderControlState>">
  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.
</ParamField>

<ParamField body="Control.style" type="CSSProperties | ((state: SliderControlState) => CSSProperties | undefined)">
  Style applied to the element, or a function that
  returns a style object based on the component's state.
</ParamField>

### Slider.Track

<ParamField body="Track.className" type="string | ((state: SliderTrackState) => string | undefined)">
  CSS class applied to the element, or a function that
  returns a class based on the component's state.
</ParamField>

<ParamField body="Track.render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SliderTrackState>">
  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.
</ParamField>

<ParamField body="Track.style" type="CSSProperties | ((state: SliderTrackState) => CSSProperties | undefined)">
  Style applied to the element, or a function that
  returns a style object based on the component's state.
</ParamField>

### Slider.Indicator

<ParamField body="Indicator.className" type="string | ((state: SliderIndicatorState) => string | undefined)">
  CSS class applied to the element, or a function that
  returns a class based on the component's state.
</ParamField>

<ParamField body="Indicator.render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SliderIndicatorState>">
  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.
</ParamField>

<ParamField body="Indicator.style" type="CSSProperties | ((state: SliderIndicatorState) => CSSProperties | undefined)">
  Style applied to the element, or a function that
  returns a style object based on the component's state.
</ParamField>

### Slider.Thumb

<ParamField body="Thumb.aria-valuetext" type="string">
  A string value forwarded to the [`aria-valuetext`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-valuetext) attribute of the `input`.
  Ignored when `getAriaValueText` is provided.
</ParamField>

<ParamField body="Thumb.className" type="string | ((state: SliderThumbState) => string | undefined)">
  CSS class applied to the element, or a function that
  returns a class based on the component's state.
</ParamField>

<ParamField body="Thumb.disabled" type="boolean" default="false">
  Whether the thumb should ignore user interaction.
</ParamField>

<ParamField body="Thumb.getAriaLabel" type="((index: number) => string) | null">
  A function which returns a string value for the [`aria-label`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-label) attribute of the `input`.
</ParamField>

<ParamField body="Thumb.getAriaValueText" type="((formattedValue: string, value: number, index: number) => string) | null">
  A function which returns a string value for the [`aria-valuetext`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-valuetext) attribute of the `input`.
  This is important for screen reader users.
</ParamField>

<ParamField body="Thumb.index" type="number">
  The index of the thumb which corresponds to the index of its value in the
  `value` or `defaultValue` array.
  This prop is required to support server-side rendering for range sliders
  with multiple thumbs.
</ParamField>

<ParamField body="Thumb.inputRef" type="Ref<HTMLInputElement>">
  A ref to access the nested input element.
</ParamField>

<ParamField body="Thumb.onBlur" type="FocusEventHandler<HTMLInputElement>">
  A blur handler forwarded to the `input`.
</ParamField>

<ParamField body="Thumb.onFocus" type="FocusEventHandler<HTMLInputElement>">
  A focus handler forwarded to the `input`.
</ParamField>

<ParamField body="Thumb.onKeyDown" type="KeyboardEventHandler<HTMLInputElement>">
  A keydown handler forwarded to the `input`.
</ParamField>

<ParamField body="Thumb.render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SliderThumbState>">
  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.
</ParamField>

<ParamField body="Thumb.style" type="CSSProperties | ((state: SliderThumbState) => CSSProperties | undefined)">
  Style applied to the element, or a function that
  returns a style object based on the component's state.
</ParamField>

<ParamField body="Thumb.tabIndex" type="number">
  Optional tab index attribute forwarded to the `input`.
</ParamField>

### Slider.Label

<ParamField body="Label.className" type="string | ((state: SliderRootState) => string | undefined)">
  CSS class applied to the element, or a function that
  returns a class based on the component's state.
</ParamField>

<ParamField body="Label.render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SliderRootState>">
  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.
</ParamField>

<ParamField body="Label.style" type="CSSProperties | ((state: SliderRootState) => CSSProperties | undefined)">
  Style applied to the element, or a function that
  returns a style object based on the component's state.
</ParamField>

### Slider.Value

<ParamField body="Value.className" type="string | ((state: SliderValueState) => string | undefined)">
  CSS class applied to the element, or a function that
  returns a class based on the component's state.
</ParamField>

<ParamField body="Value.render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SliderValueState>">
  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.
</ParamField>

<ParamField body="Value.style" type="CSSProperties | ((state: SliderValueState) => CSSProperties | undefined)">
  Style applied to the element, or a function that
  returns a style object based on the component's state.
</ParamField>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.