Skip to content

Slider

Terminal window
$ npx shadcn@latest add https://lacodda.github.io/dowel/r/slider.json
See it liveA single value, a range, one with its value shown, and a vertical one.→

Reach for it when the number does not matter much — a volume, an opacity, a weight in a search filter. Where the exact figure does matter, a NumberField is the better control: a slider cannot be typed into, cannot be pasted into, and has no state for empty.

A range is the same component with an array. Not a second component: a range slider is one track with two thumbs, and splitting them would double the styling and let the two drift apart.

<Slider value={volume} onValueChange={setVolume} /> {/* one */}
<Slider value={[min, max]} onValueChange={setRange} /> {/* two */}

Name every thumb. The thumb is the control — a hidden <input type="range"> — and aria-label on the root names the group around it, leaving each thumb unnamed. Without a name a reader lands on the control and is told a number and nothing else. The group’s label is used as a fallback, which is right for one thumb and merely adequate for two:

<Slider
value={[min, max]}
aria-label="Price"
getThumbLabel={(i) => (i === 0 ? 'Lowest price' : 'Highest price')}
/>

onValueCommitted fires once, when the drag ends. For the expensive thing a product does not want to run on every pixel of movement — a query, a save, a re-render of something large.

The hit area is bigger than the track. The Control is what a pointer aims at and it is much taller than the 6px the reader sees; making the drawn track the hit area is the commonest way a slider ends up hard to grab.

Props

Read from the types in ui/slider.tsx, the file a product copies - so the description of each prop is also what an editor shows over it there.

PropTypeDefault
valuenumber | readonly number[]A number for one thumb, an array for a range.
defaultValuenumber | readonly number[]The starting value for an uncontrolled slider: a number, or an array for a range.
onValueChange(value: number | readonly number[]) => voidCalled on every change while the thumb moves, with a number or an array to match value.
onValueCommitted(value: number | readonly number[]) => voidFires once when the drag ends, for the expensive thing a product does not want to run on every pixel of movement.
minnumberThe lowest value; Base UI's default is 0.
maxnumberThe highest value; Base UI's default is 100.
stepnumberThe increment a thumb moves by; Base UI's default is 1.
minStepsBetweenValuesnumberHow close the thumbs of a range may come, in steps.
formatNumberFormatOptionsHow the value reads when it is shown: Intl.NumberFormat options.
orientationhorizontal | verticalThe direction the track runs. A vertical slider is a fixed h-40 tall.
showValuebooleanfalseShow the current value beside the track. Off by default: on a row of settings the numbers are noise, and where the figure matters a NumberField is the better control.
getThumbLabel(index: number) => stringWhat each thumb is called, by index.
disabledbooleanStops the slider taking input.
namestringThe field name, for a form that posts the value.
aria-labelstringNames the slider as a group, and is the fallback label of every thumb.
classNamestring

Accessibility

The examples on the stand, through axe 4.14.0 in a browser, in both themes, against WCAG 2.2 A and AA and axe's best practices: 17 rules applied and passed, and none failed. The accessibility report has every item.

History

Each release that changed ui/slider.tsx - the file a product copies - newest first.

  1. v0.41.0
    • docs read every props table from the component's types ee3b159
  2. v0.17.0
    • feat numbers, scales, and the two fields with no foundation ead7461