Skip to content
UI

Slider

Picks a value or a range of values by dragging a thumb along a track.

@zenoxai/ui/components/sliderBase UI Markdown
<div className="grid w-full max-w-xs gap-3">
  <Label id="risk-threshold-label">Risk score threshold</Label>
  <Slider defaultValue={[70]} aria-labelledby="risk-threshold-label" />
</div>

Usage

import { Label } from "@zenoxai/ui/components/label"
import { Slider } from "@zenoxai/ui/components/slider"

<Label id="risk-threshold-label">Risk score threshold</Label>
<Slider defaultValue={[70]} aria-labelledby="risk-threshold-label" />

Use a slider when an approximate value is good enough and seeing it relative to the range helps: thresholds, sampling rates, score ranges. When the user needs an exact value, use a Number Input, or pair the slider with one.

Pass a number for one thumb (defaultValue={70}) or an array with one entry per thumb ([4, 8.9] for a range). Without a value the slider has one thumb.

min and max default to 0 and 100; step defaults to 1. The slider fills the width of its container.

Examples

With the value

The slider does not print its value. Keep it in state with value and onValueChange and show it next to the label. onValueChange is typed number | number[]; with an array value it always receives an array.

25%
function SliderWithValue() {
  const [sampling, setSampling] = React.useState([25])

  return (
    <div className="grid w-full max-w-xs gap-3">
      <div className="flex items-center justify-between">
        <Label id="sampling-label">Log sampling rate</Label>
        <span className="text-sm text-text-secondary tabular-nums">
          {sampling[0]}%
        </span>
      </div>
      <Slider
        value={sampling}
        onValueChange={(value) => setSampling(value as number[])}
        step={5}
        aria-labelledby="sampling-label"
      />
    </div>
  )
}

Range

Two entries render two thumbs. minStepsBetweenValues keeps them a number of steps apart.

4.0 to 8.9
function SliderRange() {
  const [range, setRange] = React.useState([4, 8.9])

  return (
    <div className="grid w-full max-w-xs gap-3">
      <div className="flex items-center justify-between">
        <Label id="cvss-label">CVSS score</Label>
        <span className="text-sm text-text-secondary tabular-nums">
          {range[0]?.toFixed(1)} to {range[1]?.toFixed(1)}
        </span>
      </div>
      <Slider
        value={range}
        onValueChange={(value) => setRange(value as number[])}
        min={0}
        max={10}
        step={0.1}
        minStepsBetweenValues={5}
        aria-labelledby="cvss-label"
      />
    </div>
  )
}

Disabled

<div className="grid w-full max-w-xs gap-3">
  <Label id="retention-label">Retention (set by your plan)</Label>
  <Slider
    defaultValue={[30]}
    max={90}
    disabled
    aria-labelledby="retention-label"
  />
</div>

Accessibility

  • Each thumb is a native <input type="range">. Name the slider with aria-labelledby pointing at a visible label’s id; the slider passes it to every thumb. To name thumbs individually (“Minimum”, “Maximum”), pass thumbLabels. aria-label on Slider and htmlFor on a label do not reach the thumbs.
  • A thumb shows the focus ring when its input has keyboard focus, the same 3px ring as the other form controls.
  • Arrow keys move a thumb by step; Shift plus an arrow, Page Up and Page Down by largeStep (10 by default); Home and End to min and max.
  • In a range, screen readers announce the thumbs as “start range” and “end range”.
  • Use onValueCommitted instead of onValueChange for expensive work, such as a request: it fires once when the user releases the thumb.

API reference

Generated from the TypeScript source. className, style and children are accepted everywhere and omitted below.

Slider

Slider picks a number, or a range with two thumbs, by dragging along a track: a threshold, a volume, a price range. When the exact figure matters, use NumberInput, alone or next to it.

PropTypeDefault
defaultValuenumber | readonly number[]

The uncontrolled value of the slider when it's initially rendered.

-
disabledboolean

Whether the slider should ignore user interaction.

false
formstring

Identifies the form that owns the slider inputs. Useful when the slider is rendered outside the form.

-
formatNumberFormatOptions

Options to format the value.

-
largeStepnumber

The granularity with which the slider can step through values when using Page Up/Page Down or Shift + Arrow Up/Arrow Down.

10
localeLocalesArgument

The locale used by `Intl.NumberFormat` when formatting the value. Defaults to the user's runtime locale.

-
maxnumber

The maximum allowed value of the slider. Should not be equal to min.

100
minnumber

The minimum allowed value of the slider. Should not be equal to max.

0
minStepsBetweenValuesnumber

The minimum steps between values in a range slider.

0
namestring

Identifies the field when a form is submitted.

-
onValueChange((value: number | readonly number[], 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.

-
onValueCommitted((value: number | readonly number[], 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.

-
orientation"horizontal" | "vertical"

The component orientation.

'horizontal'
renderReactElement<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.

-
stepnumber

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.

1
thumbAlignment"center" | "edge" | "edge-client-only"

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

'center'
thumbCollisionBehavior"none" | "push" | "swap"

Controls how thumbs behave when they collide during pointer interactions.

'push'
thumbLabelsstring[]

Already-translated accessible name of each thumb, in order.

-
valuenumber | readonly number[]

The value of the slider. For range sliders, provide an array with one value per thumb.

-