# Slider

> A React slider for a number or a range: any number of thumbs, labelled marks, a value bubble, vertical mode, tones and finger-sized thumbs.

- Type: React component (Inputs)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add slider`
- Docs: https://wingo-ui.com/components/slider

## Interaction

Drag a thumb or tap anywhere on the track to jump there; the thumb grows a little under the finger and a bubble with the formatted value rides above it, never under the finger. With a mouse the thumb shows a soft halo and its bubble on hover. Tab to a thumb for a focus ring and the bubble, then use the arrow keys for one step, Shift+arrow or PageUp / PageDown for ten, and Home / End for the ends; a range has one tab stop per thumb and they never cross. Tap a mark's label (New, Like new, 25 km) to send the nearest thumb there. On a phone each thumb has a 44px target, the whole row answers to a tap, and a sideways drag never scrolls the page. The playground filter counts the matching listings live and Reset puts both sliders back.

## Installation

```bash
npx wingo-ui@latest add slider
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/slider` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `components/ui/slider.tsx`

npm dependencies: `npm i @radix-ui/react-slider motion`

Wingo UI dependencies (installed automatically):

- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))
- [Field](https://wingo-ui.com/components/field) ([Markdown](https://wingo-ui.com/components/field.md))
- [useControllableState](https://wingo-ui.com/components/use-controllable-state) ([Markdown](https://wingo-ui.com/components/use-controllable-state.md))
- [useLocale](https://wingo-ui.com/components/use-locale) ([Markdown](https://wingo-ui.com/components/use-locale.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [Number format](https://wingo-ui.com/components/number-format) ([Markdown](https://wingo-ui.com/components/number-format.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))

## Usage

```tsx
import { useState } from "react"
import { Slider } from "@/components/ui/slider"

const usd = { style: "currency", currency: "USD", maximumFractionDigits: 0 } as const

export function PriceFilter({ onApply }: { onApply: (range: number[]) => void }) {
  const [price, setPrice] = useState([500, 2000])
  return (
    <div className="flex flex-col gap-6">
      <Slider
        label="Price"
        value={price}
        onValueChange={setPrice}
        onValueCommit={onApply}
        min={0}
        max={5000}
        step={50}
        minStepsBetweenThumbs={2}
        locale="en-US"
        format={usd}
        showValueLabel
      />
      <Slider
        label="Search radius"
        defaultValue={[25]}
        min={5}
        max={100}
        step={5}
        suffix="km"
        marks={[5, 25, 50, 75, 100].map((value) => ({ value, label: `${value} km` }))}
        showValueLabel
      />
    </div>
  )
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `label?` | `ReactNode` | `""` | The field label. One thumb is labelled by it directly (a click on it focuses the thumb); two or more thumbs are named as a group by it and each thumb gets its own name from thumbLabels. |
| `description?` | `ReactNode` | `""` | Helper text under the slider, linked to every thumb with aria-describedby. The error takes its place while it shows. |
| `error?` | `ReactNode` | `""` | Replaces the description with the animated error, turns the range red and sets aria-invalid on the thumbs. |
| `required?` | `boolean` | `false` | The required mark after the label. A slider always holds a value, so it only marks the field. |
| `invalid?` | `boolean` | `false` | The red range and aria-invalid without a message, for forms that show the error elsewhere. |
| `disabled?` | `boolean` | `false` | Dims to 50%, takes the thumbs out of the tab order and ignores every drag and tap. A disabled FieldGroup disables it too. |
| `readOnly?` | `boolean` | `false` | Shows the value and keeps the thumbs focusable (so the value can be read), but ignores drags, taps and keys. |
| `min?` | `number` | `0` | The lowest value. The playground shows its dollar sample (0 to 5,000, step 50) while min, max and step are all at their defaults. |
| `max?` | `number` | `100` | The highest value. A max at or below min falls back to min + step. |
| `step?` | `number` | `1` | The distance between values, for drags and arrow keys. A step that does not divide the range still reaches max: a drag past the middle of the last gap lands on it. |
| `minStepsBetweenThumbs?` | `number` | `0` | How many steps a range keeps between its thumbs. The playground names the gap under the slider. |
| `size?` | `"sm" \| "md" \| "lg"` | `"md"` | Track 4 / 6 / 8px, thumb 16 / 20 / 24px, and the value label's type. Every thumb gets a 44px hit area on touch screens. |
| `tone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | The range fill and the hover halo. neutral is black in light mode and white in dark mode. |
| `color?` | `string` | `""` | Any CSS color for the range; replaces the tone. Marks inside the range pick a readable dot color on their own. |
| `marks?` | `boolean \| number[] \| SliderMark[]` | `false` | Dots on the track. true puts one on every step when there are 20 steps or fewer, else five evenly spaced; number[] places them; SliderMark[] ({ value, label }) adds a label under each. Marks outside min..max are ignored. |
| `showMarkLabels?` | `boolean` | `false` | Writes the formatted value under every mark that has no label of its own. The label under a thumb's value turns to the text color. |
| `showValue?` | `"auto" \| "always" \| "never"` | `"auto"` | The value bubble above each thumb (beside it when vertical). auto shows it while dragging, on hover and on keyboard focus; always keeps it up and, under a label, makes room for it so it never covers the label row. |
| `showValueLabel?` | `boolean` | `false` | The formatted value at the end of the label row: "$1,250", or both ends of a range joined by an en dash, with a shared unit written once after the second number. Without a label it gets a row of its own. |
| `orientation?` | `"horizontal" \| "vertical"` | `"horizontal"` | vertical stands 180px tall with the mark labels beside the track and the bubble to the side. Size it with classNames.box. |
| `inverted?` | `boolean` | `false` | Fills from the other end: right to left, or top to bottom when vertical. Arrow keys follow. |
| `locale?` | `string` | `""` | Locale for every number shown (any BCP 47 tag). Empty uses the nearest LocaleProvider, then the page's language (en-US on the server). The demo passes en-US while it is empty. |
| `prefix?` | `string` | `""` | A simple unit before the number when there is no format: "~", "$". Letters get a space ("USD 50"), symbols do not ("$50"). The demo drops its dollar format while one is set. |
| `suffix?` | `string` | `""` | A simple unit after the number when there is no format: "km", "%", "m²". Letters get a non-breaking space ("50 km"), symbols do not ("50%"). |
| `format?` | `Intl.NumberFormatOptions` |  | Intl options for the bubble, the value label and the mark labels. Dollars: { style: "currency", currency: "USD", maximumFractionDigits: 0 }. Without it numbers keep the step's decimals. |
| `formatValue?` | `(value: number) => ReactNode` |  | Replaces the formatted number everywhere it shows, e.g. a word per step (New, Like new, Good, Worn). A string result is also what screen readers hear. |
| `formatValueLabel?` | `(values: number[]) => ReactNode` |  | Replaces the whole value label, e.g. "Any" when the range covers everything. |
| `thumbLabels?` | `string[]` |  | Each thumb's aria-label. Two thumbs default to "Minimum" and "Maximum", one thumb to the field label (or aria-label), three or more to "Value 1 of 3"… |
| `value?` | `number[]` |  | The controlled values, one per thumb. A value outside min..max is drawn at the edge and never corrected behind your back. |
| `defaultValue?` | `number[]` |  | The uncontrolled start, clamped into the range once. Defaults to the middle of min..max ([50] for 0..100). Two numbers make a range. |
| `onValueChange?` | `(value: number[]) => void` |  | Every change while dragging, tapping or pressing keys. |
| `onValueCommit?` | `(value: number[]) => void` |  | When a drag, a tap or a key press ends: the moment to fetch results or save. |
| `name?` | `string` | `""` | Form field name. Inside a <form> each thumb submits a hidden input (name[] for ranges), and a form reset puts the thumbs back. |
| `id?` | `string` |  | The thumb's id (the group's for ranges). Defaults to useId(); the label points at it. |
| `fieldProps?` | `Partial<FieldProps>` |  | Anything else for the Field wrapper: optional, labelAction, orientation, labelWidth, hideLabel. |
| `classNames?` | `Partial<Record<"box" \| "root" \| "track" \| "range" \| "thumb" \| "mark" \| "markLabel" \| "value" \| "valueLabel" \| "field" \| "label" \| "description" \| "error", string>>` |  | Extra classes for the inner parts: box (the slider and its mark labels; size a vertical slider here), the radix root, track, range, thumbs, mark dots and labels, the bubble, the value label and the field parts. |
| `className?` | `string` |  | Classes for the outermost element: the Field root when the slider has a label, description or error, else the slider's box. |

## Examples

- **Value always on**: showValue="always" keeps the bubble up, like a volume control. The icons jump to the ends.
- **Range with a minimum gap**: minStepsBetweenThumbs keeps the two ends apart; the shared unit is written once in the value label.
- **Labelled scale**: SliderMark labels under the track and formatValue for the bubble. Tap a word to jump there.
- **Vertical**: orientation="vertical" with mark labels beside the track, driving a picture of the block.
- **Sizes, tones and a custom color**: Track 4 / 6 / 8px and thumb 16 / 20 / 24px; any tone or CSS color for the range, marks adapt.
- **Two locales**: The same range through Intl: 500 – 2.000 lei in ro-RO next to $500 – $2,000 in en-US.
- **Invalid**: The range turns red and the error slides in; drag the ends apart and it slides away.
- **Price histogram**: A bare range under a histogram that shares its tone, the classic marketplace price filter.

## See also

- [Field](https://wingo-ui.com/components/field) ([Markdown](https://wingo-ui.com/components/field.md))
- [Number format](https://wingo-ui.com/components/number-format) ([Markdown](https://wingo-ui.com/components/number-format.md))
- [useLocale](https://wingo-ui.com/components/use-locale) ([Markdown](https://wingo-ui.com/components/use-locale.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))

---

Canonical: https://wingo-ui.com/components/slider
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
