{"slug":"slider","title":"Slider","description":"Using the slider machine in your project.","contentType":"component","framework":"react","content":"A slider allows users to make selections from a range of values. Think of it as\na custom `<input type='range'/>` with the ability to achieve custom styling and\naccessibility.\n\n## Resources\n\n\n[Latest version: v1.35.3](https://www.npmjs.com/package/@zag-js/slider)\n[Logic Visualizer](https://zag-visualizer.vercel.app/slider)\n[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/slider)\n\n\n\n**Features**\n\n- Supports centered origin (slider starting at center, instead of start\n  position)\n- Fully managed keyboard navigation\n- Supports touch or click on track to update value\n- Supports Right-to-Left directionality\n- Supports horizontal and vertical orientations\n- Prevents text selection while dragging\n\n## Installation\n\nInstall the slider package:\n\n```bash\nnpm install @zag-js/slider @zag-js/react\n# or\nyarn add @zag-js/slider @zag-js/react\n```\n\n## Anatomy\n\nCheck the slider anatomy and part names.\n\n> Each part includes a `data-part` attribute to help identify them in the DOM.\n\n\n\n## Usage\n\nImport the slider package:\n\n```jsx\nimport * as slider from \"@zag-js/slider\"\n```\n\nThe slider package exports two key functions:\n\n- `machine` - State machine logic.\n- `connect` - Maps machine state to JSX props and event handlers.\n\n> Pass a unique `id` to `useMachine` so generated element ids stay predictable.\n\nThen use the framework integration helpers:\n\n```jsx\nimport * as slider from \"@zag-js/slider\"\nimport { useMachine, normalizeProps } from \"@zag-js/react\"\n\nexport function Slider() {\n  const service = useMachine(slider.machine, { id: \"1\", value: [0] })\n\n  const api = slider.connect(service, normalizeProps)\n\n  return (\n    <div {...api.getRootProps()}>\n      <div>\n        <label {...api.getLabelProps()}>Slider Label</label>\n        <output {...api.getValueTextProps()}>{api.value.at(0)}</output>\n      </div>\n      <div {...api.getControlProps()}>\n        <div {...api.getTrackProps()}>\n          <div {...api.getRangeProps()} />\n        </div>\n        {api.value.map((_, index) => (\n          <div key={index} {...api.getThumbProps({ index })}>\n            <input {...api.getHiddenInputProps({ index })} />\n          </div>\n        ))}\n      </div>\n    </div>\n  )\n}\n```\n\n### Changing the orientation\n\nBy default, the slider is assumed to be horizontal. To change the orientation to\nvertical, set the `orientation` property in the machine's context to `vertical`.\n\nIn this mode, the slider will use the arrow up and down keys to\nincrement/decrement its value.\n\n> Don't forget to change the styles of the vertical slider by specifying its\n> height\n\n```jsx {2}\nconst service = useMachine(slider.machine, {\n  orientation: \"vertical\",\n})\n```\n\n### Setting the initial value\n\nPass the `defaultValue` property to the machine's context to set the initial\nvalue.\n\n```jsx {2}\nconst service = useMachine(slider.machine, {\n  defaultValue: [30],\n})\n```\n\n### Controlled slider\n\nTo control the slider's value programmatically, pass the `value` and\n`onValueChange` properties.\n\n```tsx\nimport { useState } from \"react\"\n\nexport function ControlledSlider() {\n  const [value, setValue] = useState([30])\n\n  const service = useMachine(slider.machine, {\n    value,\n    onValueChange(details) {\n      setValue(details.value)\n    },\n  })\n\n  return (\n    // ...\n  )\n}\n\n```\n\n### Specifying the minimum and maximum\n\nBy default, the minimum is `0` and the maximum is `100`. If that's not what you\nwant, you can easily specify different bounds by changing the values of the min\nand/or max attributes.\n\nFor example, to ask the user for a value between `-10` and `10`, you can use:\n\n```jsx {2-3}\nconst service = useMachine(slider.machine, {\n  min: -10,\n  max: 10,\n})\n```\n\n### Setting the value's granularity\n\nBy default, the granularity, is `1`, meaning that the value is always an\ninteger. You can change the step attribute to control the granularity.\n\nFor example, If you need a value between `5` and `10`, accurate to two decimal\nplaces, you should set the value of step to `0.01`:\n\n```jsx {4}\nconst service = useMachine(slider.machine, {\n  min: 5,\n  max: 10,\n  step: 0.01,\n})\n```\n\n### Listening for changes\n\nWhen the slider value changes, the `onValueChange` and `onValueChangeEnd`\ncallbacks are invoked. You can use this to setup custom behaviors in your app.\n\n```jsx {2-7}\nconst service = useMachine(slider.machine, {\n  onValueChange(details) {\n    // details => { value: number[] }\n    console.log(\"value is changing to:\", details.value)\n  },\n  onValueChangeEnd(details) {\n    // details => { value: number[] }\n    console.log(\"value has changed to:\", details.value)\n  },\n})\n```\n\n### Listening for thumb focus changes\n\nUse `onFocusChange` to track which thumb is focused.\n\n```jsx\nconst service = useMachine(slider.machine, {\n  onFocusChange(details) {\n    // details => { focusedIndex: number, value: number[] }\n    console.log(\"focused thumb:\", details.focusedIndex)\n  },\n})\n```\n\n### Changing the start position\n\nBy default, the slider's origin is at the `start` position (left in LTR and\nright in RTL). Change it by setting the `origin` property to these values:\n\n- `start`: the track will be filled from start to the thumb (default).\n- `center`: the track will be filled from the center (50%) to the thumb.\n- `end`: the track will be filled from the thumb to the end.\n\nThis applies to sliders with single values.\n\nIn scenarios where the value represents an offset (or relative value) on a\ndiverging scale, it might be useful to change the origin to center. To do this,\nset the `origin` context property to `center`.\n\n```jsx {2}\nconst service = useMachine(slider.machine, {\n  origin: \"center\",\n})\n```\n\nIn scenarios where the slider value is used as a threshold to include values\nabove it, it might make more sense to set the `origin` to `end` to have the\ntrack filled from the thumb to the end.\n\n### Changing the thumb alignment\n\nBy default, the thumb is aligned to the start of the track. Set the\n`thumbAlignment` context property to `contain` or `center`.\n\n- `center`: the thumb will extend beyond the bounds of the slider track.\n- `contain`: the thumb will be contained within the bounds of the track.\n\n```jsx {2}\nconst service = useMachine(slider.machine, {\n  thumbAlignment: \"center\",\n})\n```\n\nIf you use `contain` alignment, you might need to set the thumb size to prevent\nany flickering.\n\n```jsx {3}\nconst service = useMachine(slider.machine, {\n  thumbAlignment: \"contain\",\n  thumbSize: { width: 20, height: 20 },\n})\n```\n\n### Thumb collision behavior\n\nFor multi-thumb sliders, use `thumbCollisionBehavior` to control what happens\nwhen thumbs collide.\n\n```jsx\nconst service = useMachine(slider.machine, {\n  defaultValue: [20, 80],\n  thumbCollisionBehavior: \"swap\", // \"none\" | \"push\" | \"swap\"\n})\n```\n\n### Usage within forms\n\nTo use slider in forms, set `name` and render `api.getHiddenInputProps()`.\n\n```jsx {2}\nconst service = useMachine(slider.machine, {\n  name: \"quantity\",\n})\n```\n\n### Customizing value text for screen readers\n\nUse `getAriaValueText` to customize `aria-valuetext` per thumb.\n\n```jsx\nconst service = useMachine(slider.machine, {\n  getAriaValueText(details) {\n    return `Value ${details.index + 1}: ${details.value}`\n  },\n})\n```\n\n### RTL Support\n\nThe slider has built-in support for RTL alignment and interaction. In the RTL\nmode, operations are performed from right to left, meaning, the left arrow key\nwill increment and the right arrow key will decrement.\n\nTo enable RTL support, pass the `dir: rtl` context property\n\n```jsx {2}\nconst service = useMachine(slider.machine, {\n  dir: \"rtl\",\n})\n```\n\n> While we take care of the interactions in RTL mode, you'll have to ensure you\n> apply the correct CSS styles to flip the layout.\n\n### Using slider marks\n\nTo show marks or ticks along the slider track, use the exposed\n`api.getMarkerProps()` method to position the slider marks relative to the\ntrack.\n\n```jsx {11-15}\n//...\n<div>\n  <div {...api.getControlProps()}>\n    <div {...api.getTrackProps()}>\n      <div {...api.getRangeProps()} />\n    </div>\n    {api.value.map((_, index) => (\n      <div key={index} {...api.getThumbProps({ index })}>\n        <input {...api.getHiddenInputProps({ index })} />\n      </div>\n    ))}\n  </div>\n  <div {...api.getMarkerGroupProps()}>\n    <span {...api.getMarkerProps({ value: 10 })}>|</span>\n    <span {...api.getMarkerProps({ value: 30 })}>|</span>\n    <span {...api.getMarkerProps({ value: 90 })}>|</span>\n  </div>\n</div>\n//...\n```\n\n## Styling guide\n\nEach slider part includes a `data-part` attribute you can target in CSS.\n\n### Focused State\n\nWhen the slider thumb is focused, the `data-focus` attribute is added to the\nroot, control, thumb and label parts.\n\n```css\n[data-part=\"root\"][data-focus] {\n  /* styles for root focus state */\n}\n\n[data-part=\"thumb\"]:focus {\n  /* styles for thumb focus state */\n}\n\n[data-part=\"control\"][data-focus] {\n  /* styles for control focus state */\n}\n\n[data-part=\"track\"][data-focus] {\n  /* styles for track focus state */\n}\n\n[data-part=\"range\"][data-focus] {\n  /* styles for range focus state */\n}\n```\n\n### Disabled State\n\nWhen the slider is disabled, the `data-disabled` attribute is added to the root,\nlabel, control and thumb.\n\n```css\n[data-part=\"root\"][data-disabled] {\n  /* styles for root disabled state */\n}\n\n[data-part=\"label\"][data-disabled] {\n  /* styles for label disabled state */\n}\n\n[data-part=\"control\"][data-disabled] {\n  /* styles for control disabled state */\n}\n\n[data-part=\"value-text\"][data-disabled] {\n  /* styles for output disabled state */\n}\n\n[data-part=\"thumb\"][data-disabled] {\n  /* styles for thumb disabled state */\n}\n\n[data-part=\"range\"][data-disabled] {\n  /* styles for thumb disabled state */\n}\n```\n\n### Invalid State\n\nWhen the slider is invalid, the `data-invalid` attribute is added to the root,\ntrack, range, label, and thumb parts.\n\n```css\n[data-part=\"root\"][data-invalid] {\n  /* styles for root invalid state */\n}\n\n[data-part=\"label\"][data-invalid] {\n  /* styles for label invalid state */\n}\n\n[data-part=\"control\"][data-invalid] {\n  /* styles for control invalid state */\n}\n\n[data-part=\"valueText\"][data-invalid] {\n  /* styles for output invalid state */\n}\n\n[data-part=\"thumb\"][data-invalid] {\n  /* styles for thumb invalid state */\n}\n\n[data-part=\"range\"][data-invalid] {\n  /* styles for range invalid state */\n}\n```\n\n### Orientation\n\n```css\n[data-part=\"root\"][data-orientation=\"(horizontal|vertical)\"] {\n  /* styles for horizontal or vertical  */\n}\n\n[data-part=\"thumb\"][data-orientation=\"(horizontal|vertical)\"] {\n  /* styles for horizontal or vertical  */\n}\n\n[data-part=\"track\"][data-orientation=\"(horizontal|vertical)\"] {\n  /* styles for horizontal or vertical  */\n}\n```\n\n### Styling the markers\n\n```css\n[data-part=\"marker\"][data-state=\"(at|under|over)-value\"] {\n  /* styles for when the value exceeds the marker's value */\n}\n```\n\n## Methods and Properties\n\n### Machine Context\n\nThe slider machine exposes the following context properties:\n\n**`ids`**\nType: `Partial<{ root: string; thumb: (index: number) => string; hiddenInput: (index: number) => string; control: string; track: string; range: string; label: string; valueText: string; marker: (index: number) => string; }>`\nDescription: The ids of the elements in the slider. Useful for composition.\n\n**`aria-label`**\nType: `string[]`\nDescription: The aria-label of each slider thumb. Useful for providing an accessible name to the slider\n\n**`aria-labelledby`**\nType: `string[]`\nDescription: The `id` of the elements that labels each slider thumb. Useful for providing an accessible name to the slider\n\n**`name`**\nType: `string`\nDescription: The name associated with each slider thumb (when used in a form)\n\n**`form`**\nType: `string`\nDescription: The associate form of the underlying input element.\n\n**`value`**\nType: `number[]`\nDescription: The controlled value of the slider\n\n**`defaultValue`**\nType: `number[]`\nDescription: The initial value of the slider when rendered.\nUse when you don't need to control the value of the slider.\n\n**`disabled`**\nType: `boolean`\nDescription: Whether the slider is disabled\n\n**`readOnly`**\nType: `boolean`\nDescription: Whether the slider is read-only\n\n**`invalid`**\nType: `boolean`\nDescription: Whether the slider is invalid\n\n**`onValueChange`**\nType: `(details: ValueChangeDetails) => void`\nDescription: Function invoked when the value of the slider changes\n\n**`onValueChangeEnd`**\nType: `(details: ValueChangeDetails) => void`\nDescription: Function invoked when the slider value change is done\n\n**`onFocusChange`**\nType: `(details: FocusChangeDetails) => void`\nDescription: Function invoked when the slider's focused index changes\n\n**`getAriaValueText`**\nType: `(details: ValueTextDetails) => string`\nDescription: Function that returns a human readable value for the slider thumb\n\n**`min`**\nType: `number`\nDescription: The minimum value of the slider\n\n**`max`**\nType: `number`\nDescription: The maximum value of the slider\n\n**`step`**\nType: `number`\nDescription: The step value of the slider\n\n**`minStepsBetweenThumbs`**\nType: `number`\nDescription: The minimum permitted steps between multiple thumbs.\n\n`minStepsBetweenThumbs` * `step` should reflect the gap between the thumbs.\n\n- `step: 1` and `minStepsBetweenThumbs: 10` => gap is `10`\n- `step: 10` and `minStepsBetweenThumbs: 2` => gap is `20`\n\n**`orientation`**\nType: `\"vertical\" | \"horizontal\"`\nDescription: The orientation of the slider\n\n**`origin`**\nType: `\"start\" | \"center\" | \"end\"`\nDescription: The origin of the slider range. The track is filled from the origin\nto the thumb for single values.\n- \"start\": Useful when the value represents an absolute value\n- \"center\": Useful when the value represents an offset (relative)\n- \"end\": Useful when the value represents an offset from the end\n\n**`thumbAlignment`**\nType: `\"center\" | \"contain\"`\nDescription: The alignment of the slider thumb relative to the track\n- `center`: the thumb will extend beyond the bounds of the slider track.\n- `contain`: the thumb will be contained within the bounds of the track.\n\n**`thumbSize`**\nType: `{ width: number; height: number; }`\nDescription: The slider thumbs dimensions\n\n**`thumbCollisionBehavior`**\nType: `\"none\" | \"push\" | \"swap\"`\nDescription: Controls how thumbs behave when they collide during pointer interactions.\n- `none` (default): Thumbs cannot move past each other; excess movement is ignored.\n- `push`: Thumbs push each other without restoring their previous positions when dragged back.\n- `swap`: Thumbs swap places when dragged past each other.\n\n**`dir`**\nType: `\"ltr\" | \"rtl\"`\nDescription: The document's text/writing direction.\n\n**`id`**\nType: `string`\nDescription: The unique identifier of the machine.\n\n**`getRootNode`**\nType: `() => ShadowRoot | Node | Document`\nDescription: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.\n\n### Machine API\n\nThe slider `api` exposes the following methods:\n\n**`value`**\nType: `number[]`\nDescription: The value of the slider.\n\n**`dragging`**\nType: `boolean`\nDescription: Whether the slider is being dragged.\n\n**`focused`**\nType: `boolean`\nDescription: Whether the slider is focused.\n\n**`setValue`**\nType: `(value: number[]) => void`\nDescription: Function to set the value of the slider.\n\n**`getThumbValue`**\nType: `(index: number) => number`\nDescription: Returns the value of the thumb at the given index.\n\n**`setThumbValue`**\nType: `(index: number, value: number) => void`\nDescription: Sets the value of the thumb at the given index.\n\n**`getValuePercent`**\nType: `(value: number) => number`\nDescription: Returns the percent of the thumb at the given index.\n\n**`getPercentValue`**\nType: `(percent: number) => number`\nDescription: Returns the value of the thumb at the given percent.\n\n**`getThumbPercent`**\nType: `(index: number) => number`\nDescription: Returns the percent of the thumb at the given index.\n\n**`setThumbPercent`**\nType: `(index: number, percent: number) => void`\nDescription: Sets the percent of the thumb at the given index.\n\n**`getThumbMin`**\nType: `(index: number) => number`\nDescription: Returns the min value of the thumb at the given index.\n\n**`getThumbMax`**\nType: `(index: number) => number`\nDescription: Returns the max value of the thumb at the given index.\n\n**`increment`**\nType: `(index: number) => void`\nDescription: Function to increment the value of the slider at the given index.\n\n**`decrement`**\nType: `(index: number) => void`\nDescription: Function to decrement the value of the slider at the given index.\n\n**`focus`**\nType: `VoidFunction`\nDescription: Function to focus the slider. This focuses the first thumb.\n\n### Data Attributes\n\n**`Label`**\n\n**`data-scope`**: slider\n**`data-part`**: label\n**`data-disabled`**: Present when disabled\n**`data-orientation`**: The orientation of the label\n**`data-invalid`**: Present when invalid\n**`data-dragging`**: Present when in the dragging state\n**`data-focus`**: Present when focused\n\n**`Root`**\n\n**`data-scope`**: slider\n**`data-part`**: root\n**`data-disabled`**: Present when disabled\n**`data-orientation`**: The orientation of the slider\n**`data-dragging`**: Present when in the dragging state\n**`data-invalid`**: Present when invalid\n**`data-focus`**: Present when focused\n\n**`ValueText`**\n\n**`data-scope`**: slider\n**`data-part`**: value-text\n**`data-disabled`**: Present when disabled\n**`data-orientation`**: The orientation of the valuetext\n**`data-invalid`**: Present when invalid\n**`data-focus`**: Present when focused\n\n**`Track`**\n\n**`data-scope`**: slider\n**`data-part`**: track\n**`data-disabled`**: Present when disabled\n**`data-invalid`**: Present when invalid\n**`data-dragging`**: Present when in the dragging state\n**`data-orientation`**: The orientation of the track\n**`data-focus`**: Present when focused\n\n**`Thumb`**\n\n**`data-scope`**: slider\n**`data-part`**: thumb\n**`data-index`**: The index of the item\n**`data-name`**: \n**`data-disabled`**: Present when disabled\n**`data-orientation`**: The orientation of the thumb\n**`data-focus`**: Present when focused\n**`data-dragging`**: Present when in the dragging state\n\n**`Range`**\n\n**`data-scope`**: slider\n**`data-part`**: range\n**`data-dragging`**: Present when in the dragging state\n**`data-focus`**: Present when focused\n**`data-invalid`**: Present when invalid\n**`data-disabled`**: Present when disabled\n**`data-orientation`**: The orientation of the range\n\n**`Control`**\n\n**`data-scope`**: slider\n**`data-part`**: control\n**`data-dragging`**: Present when in the dragging state\n**`data-disabled`**: Present when disabled\n**`data-orientation`**: The orientation of the control\n**`data-invalid`**: Present when invalid\n**`data-focus`**: Present when focused\n\n**`MarkerGroup`**\n\n**`data-scope`**: slider\n**`data-part`**: marker-group\n**`data-orientation`**: The orientation of the markergroup\n\n**`Marker`**\n\n**`data-scope`**: slider\n**`data-part`**: marker\n**`data-orientation`**: The orientation of the marker\n**`data-value`**: The value of the item\n**`data-disabled`**: Present when disabled\n**`data-state`**: \n\n**`DraggingIndicator`**\n\n**`data-scope`**: slider\n**`data-part`**: dragging-indicator\n**`data-orientation`**: The orientation of the draggingindicator\n**`data-state`**: \"open\" | \"closed\"\n\n### CSS Variables\n\n<CssVarTable name=\"slider\" />\n\n## Accessibility\n\nAdheres to the\n[Slider WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/slider).\n\n### Keyboard Interactions\n\n**`ArrowRight`**\nDescription: <span>Increments the slider based on defined step</span>\n\n**`ArrowLeft`**\nDescription: <span>Decrements the slider based on defined step</span>\n\n**`ArrowUp`**\nDescription: <span>Increases the value by the step amount.</span>\n\n**`ArrowDown`**\nDescription: <span>Decreases the value by the step amount.</span>\n\n**`PageUp`**\nDescription: <span>Increases the value by a larger step</span>\n\n**`PageDown`**\nDescription: <span>Decreases the value by a larger step</span>\n\n**`Shift + ArrowUp`**\nDescription: <span>Increases the value by a larger step</span>\n\n**`Shift + ArrowDown`**\nDescription: <span>Decreases the value by a larger step</span>\n\n**`Home`**\nDescription: Sets the value to its minimum.\n\n**`End`**\nDescription: Sets the value to its maximum.","package":"@zag-js/slider","editUrl":"https://github.com/chakra-ui/zag/edit/main/website/data/components/slider.mdx"}