Slider
Single-value or two-thumb range input on top of Ark UI's slider primitives. Marks, floating label, sizes, colors — keyboard, RTL, and ARIA handled upstream.
The Slider wraps Ark UI's Slider state machine, so keyboard navigation (←/→, Home/End, PageUp/PageDown), RTL flipping, and ARIA wiring come for free. The wrapper layers a styled track / range / thumb, optional marks, and a floating label that follows each thumb.
Pass a number for a single thumb, or a [number, number] tuple for a two-thumb range. The value shape flows through the whole API — the callbacks echo back the same shape.
import { Slider } from '@42/ui-react/slider';
<Slider defaultValue={42} label="42%" /> {/* single thumb */}
<Slider defaultValue={[25, 60]} /> {/* two-thumb range */}Playground
Component
Dir
Presets
Modes
Props
Code
<Component size="md" />Sizes
Five track thicknesses — xs through xl. The thumb scales to match.
Code
<Slider size="xs" defaultValue={42} /><Slider size="sm" defaultValue={42} /><Slider size="md" defaultValue={42} /><Slider size="lg" defaultValue={42} /><Slider size="xl" defaultValue={42} />Colors
Color is orthogonal to the rest of the slider — the accent rides on data-color and the range/thumb reference --c-solid, so every palette in the kit flows through for free, including dark mode and consumer-defined palettes. brand is the default; see Colors for the full system.
brandgrayredorangeyellowgreentealcyanbluepurplepinkCode
import { COLORS } from '@42/ui-react';// ['brand','gray','red','orange','yellow','green','teal','cyan','blue','purple','pink']{COLORS.map((c) => (<Slider key={c} color={c} defaultValue={60} />))}Marks
Pass marks to paint dots along the track. Each mark is { value, label? } — the label is rendered below the dot. Marks don't restrict input; they're purely visual unless you pair them with a matching step.
Code
<SliderdefaultValue={50}marks={[ { value: 0, label: '0' }, { value: 25, label: '25' }, { value: 50, label: '50' }, { value: 75, label: '75' }, { value: 100, label: '100' },]}/>For a segmented stepper, combine marks with step:
<Slider
min={0}
max={10}
step={1}
defaultValue={4}
marks={Array.from({ length: 11 }, (_, i) => ({ value: i }))}
/>Min, max & step
min, max, and step define the value space — nothing here assumes 0–100. The label, marks, and keyboard steps all read from it, so a fractional step and a unit-formatted label compose without extra wiring.
Code
<Slider min={16} max={30} step={0.5} defaultValue={21} label={(v) => `${v}°C`} />Range
Pass a [number, number] tuple instead of a single number and the slider renders two thumbs with the filled range between them — for price bands, time windows, or any min/max selection. Everything else carries over: each thumb gets its own label, marks between the thumbs read as active, and the callbacks hand you back a [number, number].
Use minStepsBetweenThumbs to keep the thumbs from crossing or sitting on top of each other.
Code
<Slider defaultValue={[25, 60]} minStepsBetweenThumbs={5} />Controlled, the value stays a tuple end to end — onChange receives [number, number], no shape guessing:
'use client';
import { useState } from 'react';
import { Slider } from '@42/ui-react/slider';
export function PriceRange() {
const [range, setRange] = useState<[number, number]>([25, 60]);
return (
<Slider
value={range}
onChange={setRange}
onChangeEnd={(v) => filterByPrice(v)}
minStepsBetweenThumbs={5}
label={(v) => `$${v}`}
/>
);
}Origin
By default the filled range grows from the start of the track. Set origin="center" to fill outward from the midpoint — the right shape for balance, pan, or any signed adjustment around a neutral zero.
Code
<Slider min={-50} max={50} defaultValue={0} origin="center" />Floating label
label accepts any ReactNode and floats above the thumb. By default it appears on hover, focus, or drag (showLabelOnHover). Set it to never or false to hide it.
Code
<Slider label="35%" /> {/* on hover/focus */}<Slider label="35%" showLabel="always" /> {/* always visible */}<Slider showLabel="never" /> {/* no label */}<Slider label={null} /> {/* no label */}The label must be serializable. To bind it to the current value, drive the slider as a controlled input from a client component (see below).
Controlled
onChange fires on every change (drag, keyboard, click). onChangeEnd fires once the interaction settles — use it to debounce expensive work (network calls, expensive re-renders downstream). Drag the example below: value tracks every tick, committed only updates on release.
value 50%
committed 50%
A controlled value is also how you wire a dynamic label, since the label has to be computed in the same client tree as the slider:
'use client';
import { useState } from 'react';
import { Slider } from '@42/ui-react/slider';
export function VolumeSlider() {
const [value, setValue] = useState(50);
return (
<Slider
value={value}
onChange={setValue}
onChangeEnd={(v) => commitToServer(v)}
label={`${value}%`}
/>
);
}Disabled & read-only
disabled dims the track and blocks all interaction. readOnly keeps full contrast and stays focusable — so the value is still announced to assistive tech — but rejects changes. Reach for readOnly to show a value the user can inspect but not edit.
Code
<Slider defaultValue={60} disabled /><Slider defaultValue={60} readOnly />Forms
Each thumb renders a hidden <input>, so a name wires the slider into native <form> submission and FormData — no controlled state required.
Code
<form action={save}><Slider name="volume" defaultValue={60} /></form>Styling parts
Every part takes a class override through classNames — root, control, track, range, thumb, thumbLabel, and the mark slots. Use it to retheme a one-off without forking the component; the data-color system still drives anything you don't override.
Code
<SliderdefaultValue={65}classNames={{ track: 'bg-purple-100 dark:bg-purple-950', range: 'bg-purple-500', thumb: 'border-purple-500',}}/>API
Prop
Type
All other props are forwarded to the underlying Ark Slider.Root.
Accessibility
- Keyboard:
←/→step bystep;Shift + ←/→jumps by 10×;Home/Endsnap tomin/max;PageUp/PageDownjump by 10×. - Range: each thumb is an independent, focusable control —
Tabmoves between them, and arrows drive whichever is focused.minStepsBetweenThumbsenforces the gap so they never cross. - RTL: when the document
dirisrtl(or a parent sets it), Ark inverts the thumb direction so→still increases the value visually. - Focus ring is keyboard-only (
focus-visible:) so pointer interactions stay quiet. - A hidden
<input>is rendered inside each thumb so the slider integrates with native<form>submission. Passnameto wire it up.