Fields
Range Slider
A two-thumb slider for choosing a range between bounds.
When to use it: Use it for price bands, date spans, or any from–to pair on one scale.
Example
Price range
$20 – $60
import { useState } from "react";
import { RangeSlider, RangeSliderThumb, RangeSliderTrack } from "@comp0/react";
export function Example() {
const [value, setValue] = useState<[number, number]>([20, 60]);
return (
<div className="flex w-full max-w-xs flex-col gap-2">
<p className="text-base font-medium text-zinc-900 sm:text-sm dark:text-zinc-100">
Price range
</p>
<RangeSlider
aria-label="Price range"
className="relative h-6 w-full"
name="price"
value={value}
onChange={setValue}
>
<RangeSliderTrack className="absolute top-1/2 h-1.5 w-full -translate-y-1/2 rounded-full bg-zinc-200 dark:bg-zinc-700">
<div
className="absolute inset-y-0 rounded-full bg-teal-600 dark:bg-teal-400"
style={{
left: "calc(var(--comp0-range-slider-start) * 100%)",
width:
"calc((var(--comp0-range-slider-end) - var(--comp0-range-slider-start)) * 100%)",
}}
/>
</RangeSliderTrack>
<RangeSliderThumb
aria-label="Minimum price"
className="absolute top-1/2 size-4 -translate-x-1/2 -translate-y-1/2 rounded-full border border-zinc-950/10 bg-white shadow outline-teal-600 focus-visible:outline-2 data-dragging:border-teal-600 dark:border-white/20 dark:bg-zinc-100 dark:outline-teal-400 dark:data-dragging:border-teal-400"
style={{ left: "calc(var(--comp0-range-slider-start) * 100%)" }}
thumb="start"
/>
<RangeSliderThumb
aria-label="Maximum price"
className="absolute top-1/2 size-4 -translate-x-1/2 -translate-y-1/2 rounded-full border border-zinc-950/10 bg-white shadow outline-teal-600 focus-visible:outline-2 data-dragging:border-teal-600 dark:border-white/20 dark:bg-zinc-100 dark:outline-teal-400 dark:data-dragging:border-teal-400"
style={{ left: "calc(var(--comp0-range-slider-end) * 100%)" }}
thumb="end"
/>
</RangeSlider>
<p className="text-base text-zinc-600 tabular-nums sm:text-sm dark:text-zinc-400">
${value[0]} – ${value[1]}
</p>
</div>
);
}Anatomy
Dashed frames are invisible state providers; shaded shapes own real DOM. Numbered pins match the list below.
A wireframe sketch of the assembled component. Each numbered marker matches a part in the list that follows.
RangeSlider
Group that owns the [start, end] pair and exposes the thumb positions as --comp0-range-slider-start/--comp0-range-slider-end 0..1 fractions for styling. Owns a DOM element.
RangeSliderTrack
The rail the thumbs travel along; pressing it moves and focuses the nearest thumb. Owns a DOM element.
RangeSliderThumb
Focusable slider for one end of the range; its announced bounds interlock with the sibling so the thumbs never cross. Owns a DOM element.
Step by step
- 1
Add the main part
Wrap a RangeSliderTrack and two RangeSliderThumbs (thumb="start" and thumb="end") in RangeSlider with an aria-label.
- 2
Add the supporting parts
Give each thumb its own aria-label; the thumbs clamp at each other so the range cannot cross.
- 3
Make the behavior clear
Position the parts with the --comp0-range-slider-start and --comp0-range-slider-end fractions; pass name to submit both ends.
Exampletsx <RangeSlider name="price" defaultValue={[20, 60]} aria-label="Price range"><RangeSliderTrack /><RangeSliderThumb thumb="start" aria-label="Minimum price" /><RangeSliderThumb thumb="end" aria-label="Maximum price" /></RangeSlider>
Keyboard
- ⇥
- Moves between the two thumbs.
- →
- Increases the focused thumb by one step.
- ↑
- Increases the focused thumb by one step.
- ←
- Decreases the focused thumb by one step.
- ↓
- Decreases the focused thumb by one step.
- PgUp
- Increases the focused thumb by ten steps.
- PgDn
- Decreases the focused thumb by ten steps.
- Home
- Moves the thumb to its own minimum: min for start, the start value for end.
- End
- Moves the thumb to its own maximum: the end value for start, max for end.
Forms and accessibility
Submits two hidden inputs, `${name}-start` and `${name}-end`, carrying the pair.
Accessibility checklist
- Name the group and both thumbs: an aria-label on RangeSlider and one per RangeSliderThumb.
- Each thumb announces interlocked bounds, so screen readers hear how far it can move right now.
- Show the selected values as visible text; the colored track alone is not enough.
API reference
import { RangeSlider, RangeSliderThumb, RangeSliderTrack } from "@comp0/react";RangeSlider
DOM elementGroup that owns the [start, end] pair and exposes the thumb positions as --comp0-range-slider-start/--comp0-range-slider-end 0..1 fractions for styling.
| Prop | Type | Description |
|---|---|---|
value | [number, number] | Controlled [start, end] pair. |
defaultValue | [number, number] | Initial [start, end] pair. |
onChange | (value: [number, number]) => void | Receives the next [start, end] pair. |
min / max / step | number | Range bounds and step grid; default 0, 100, and 1. |
name | string | Submits two hidden inputs named `${name}-start` and `${name}-end`. |
form | string | Associates both hidden values with a form by id. |
orientation | "horizontal" | "vertical" | Announced direction; defaults to "horizontal". |
disabled | boolean | Disables both thumbs and the track. |
aria-label | string | Names the group for assistive technology; required. |
RangeSliderTrack
DOM elementThe rail the thumbs travel along; pressing it moves and focuses the nearest thumb.
| Prop | Type | Description |
|---|---|---|
className | string | Style the rail; position it relative to the root. |
RangeSliderThumb
DOM elementFocusable slider for one end of the range; its announced bounds interlock with the sibling so the thumbs never cross.
| Prop | Type | Description |
|---|---|---|
thumb | "start" | "end" | Which end of the range this thumb controls. |
aria-label | string | Names this thumb, such as "Minimum price"; required per thumb. |
Style hooks
Attributes that appear while a state is true. Target them with Tailwind data variants such as data-open:bg-zinc-100, or with any CSS selector.
[data-dragging]on RangeSliderThumb
- The thumb is being dragged with a captured pointer.
[data-disabled]on RangeSlider, RangeSliderThumb
- The range slider is disabled.
[data-orientation]on RangeSlider, RangeSliderTrack
- The travel direction, horizontal or vertical.
:focus-visibleon RangeSliderThumb
- The thumb has keyboard focus.