Slider
A range input with draggable thumbs. Supports single value, ranges (two thumbs), custom steps, and full keyboard control.
Stepped (step = 10)
Disabled
Installation
pnpm dlx @hex-core/cli add sliderRange slider
Two-thumb range
<Slider defaultValue={[20, 80]} max={100} step={1} />Controlled price range
Two-thumb range bound to React state — the canonical price-range or date-range filter. The value prop is always a number[], even for single-thumb usage.
import { useState } from "react";
const formatUSD = (amount: number) =>
new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", maximumFractionDigits: 0 }).format(amount);
export function PriceRangeFilter() {
const [range, setRange] = useState<[number, number]>([100, 750]);
const [min, max] = range;
return (
<div className="grid w-72 gap-3">
<div className="flex items-center justify-between">
<Label htmlFor="price" className="text-sm">Price range</Label>
<span className="text-sm tabular-nums text-muted-foreground">
{formatUSD(min)} – {formatUSD(max)}
</span>
</div>
<Slider
id="price"
min={0}
max={1000}
step={25}
value={range}
onValueChange={(next) => setRange(next as [number, number])}
aria-label="Price range"
thumbLabels={["Minimum price", "Maximum price"]}
/>
</div>
);
}API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
value | object | — | Controlled array of thumb values (number[]), e.g. [50] for single, [20, 80] for range |
defaultValue | object | — | Default array of thumb values (number[]) for uncontrolled usage |
onValueChange | function | — | Callback on value change: (value: number[]) => void |
min | number | 0 | Minimum value |
max | number | 100 | Maximum value |
step | number | 1 | Step interval between valid values |
disabled | boolean | false | Disable the slider |
orientation | "horizontal" | "vertical" | horizontal | Slider direction |
aria-label | string | — | Accessible label for the slider as a whole. Mirrored onto a single thumb automatically; for range sliders prefer thumbLabels. |
aria-labelledby | string | — | Id of an external visible label that names the slider. |
thumbLabels | object | — | Per-thumb accessible labels (string[]). Required for range sliders so each thumb has a meaningful name (e.g. ['Minimum price', 'Maximum price']). For a single-thumb slider, the Root's aria-label is mirrored onto the thumb automatically and thumbLabels is only needed when overriding that default. |
AI Guidance
When to use
Use for continuous numeric inputs with a known range: volume, brightness, price range filter, opacity. Pair value with a visible number display when the exact value matters.
When not to use
Don't use when the user needs to enter an exact number (use Input type=number). Don't use for discrete choices (use Select or RadioGroup).
Common mistakes
- Using Slider for exact values without showing the number
- Missing min/max bounds
- Using step=1 for fractional values (set step=0.01)
- Not providing aria-label / aria-labelledby when there's no visible label
- Range slider with only Root aria-label and no thumbLabels — both thumbs fall back to '(N of 2)' indexed names instead of meaningful per-thumb labels
- thumbLabels.length !== value.length — extra labels are ignored, missing labels fall back to indexed names (dev-mode warning)
Accessibility
Arrow keys step by step, Home/End jump to min/max, PageUp/PageDown step larger. Radix handles aria-valuemin/max/now. Each thumb has its own accessible name: explicit via thumbLabels[i], else mirrored from the Root's aria-label (single thumb) or indexed '(i of N)' fallback (range). Add aria-label / aria-labelledby on the Root when there's no visible label.
Related components
Token budget: 450
Verified against @hex-core/components@1.12.0