react-js-multi-range-sliders
v1.2.3
Published
React range slider components — dual-thumb, single, vertical, multi-point & circular. Ruler, tooltips, 4 themes, RTL support, TypeScript, zero dependencies.
Maintainers
Keywords
Readme
Components at a glance
| Component | Description | onChange value |
|---|---|---|
| RangeSlider | Dual-thumb horizontal | { min: number; max: number } |
| SingleSlider | Single-thumb horizontal | number |
| VerticalSlider | Single-thumb vertical | number |
| MultiPointSlider | N-thumb horizontal | number[] (sorted) |
| CircularSlider | SVG radial / knob | number |
Install
npm install react-js-multi-range-slidersPeer dependencies (install once if not already present):
npm install react react-domImport
// Named imports — bundler tree-shakes unused components automatically
import {
RangeSlider,
SingleSlider,
VerticalSlider,
MultiPointSlider,
CircularSlider,
} from "react-js-multi-range-sliders";
// Per-component import — zero bundler tree-shaking needed
import RangeSlider from "react-js-multi-range-sliders/RangeSlider";
import CircularSlider from "react-js-multi-range-sliders/CircularSlider";Styles are injected automatically when a component is imported — no separate CSS file needed.
RangeSlider
import { useState } from "react";
import { RangeSlider } from "react-js-multi-range-sliders";
function PriceFilter() {
const [range, setRange] = useState({ min: 100, max: 400 });
return (
<RangeSlider
min={0}
max={500}
step={10}
value={range}
onChange={setRange}
minDistance={20}
showTooltip
showLabels
formatLabel={(v) => `$${v}`}
theme="default"
/>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| min ✱ | number | — | Minimum bound |
| max ✱ | number | — | Maximum bound |
| onChange ✱ | (v: RangeValue) => void | — | Called on every change |
| value | RangeValue | — | Controlled value |
| defaultValue | RangeValue | — | Uncontrolled initial value |
| step | number | 1 | Step interval |
| minDistance | number | 1 | Min gap between thumbs |
| allowOverlap | boolean | false | Allow thumbs to meet |
| direction | "ltr" \| "rtl" | "ltr" | Layout direction |
| disabled | boolean | false | Read-only state |
| theme | Theme | "default" | Visual theme |
| showTooltip | boolean | false | Tooltip above each thumb |
| showLabels | boolean | true | Value labels below track |
| formatLabel | (v: number) => string | String | Custom label/tooltip text |
| onChangeStart | (v: RangeValue) => void | — | Fires on pointer-down |
| onChangeComplete | (v: RangeValue) => void | — | Fires on pointer-up |
| trackColor | string | — | Track background |
| rangeColor | string | — | Highlighted range fill |
| thumbColor | string | — | Thumb fill |
| labelColor | string | — | Label / tooltip text |
| trackStyle | CSSProperties | — | Inline style for track |
| rangeStyle | CSSProperties | — | Inline style for fill |
| thumbStyle | CSSProperties | — | Inline style for thumbs |
| labelStyle | CSSProperties | — | Inline style for labels |
| ariaLabelMin | string | "Minimum value" | ARIA label, min thumb |
| ariaLabelMax | string | "Maximum value" | ARIA label, max thumb |
| labels | string[] | — | Evenly-spaced tick labels (e.g. day or month names) |
| ruler | boolean | true when labels set | Show ruler tick marks below the track |
| subSteps | boolean \| number | false | Minor ticks between major ticks (true = 3) |
SingleSlider
import { SingleSlider } from "react-js-multi-range-sliders";
<SingleSlider
min={0}
max={100}
defaultValue={40}
onChange={(v) => console.log(v)} // v is number
showTooltip
showLabels
formatLabel={(v) => `${v}%`}
theme="material"
/>Same base props as RangeSlider (including labels, ruler, subSteps). Additional prop:
| Prop | Type | Default | Description |
|---|---|---|---|
| ariaLabel | string | "Value" | ARIA label for the thumb |
VerticalSlider
import { VerticalSlider } from "react-js-multi-range-sliders";
function Equalizer() {
const [bass, setBass] = useState(60);
const [mid, setMid] = useState(40);
const [high, setHigh] = useState(75);
return (
<div style={{ display: "flex", gap: 24 }}>
<VerticalSlider min={0} max={100} height={180} value={bass} onChange={setBass} showTooltip />
<VerticalSlider min={0} max={100} height={180} value={mid} onChange={setMid} showTooltip />
<VerticalSlider min={0} max={100} height={180} value={high} onChange={setHigh} showTooltip />
</div>
);
}Additional prop:
| Prop | Type | Default | Description |
|---|---|---|---|
| height | number \| string | 200 | Track height (px or CSS string) |
Cross-browser: writing-mode: vertical-lr (Chrome / Safari / Edge) + orient="vertical" attribute (Firefox).
MultiPointSlider
Three or more thumbs. onChange receives a sorted number[].
import { MultiPointSlider } from "react-js-multi-range-sliders";
function PriceTiers() {
const [stops, setStops] = useState([16, 38, 62, 82]);
return (
<MultiPointSlider
min={0}
max={100}
values={stops}
onChange={setStops}
minDistance={5}
showTooltip
showLabels
formatLabel={(v) => `$${v}`}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
| values ✱ | number[] | — | Controlled thumb values (sorted) |
| defaultValues | number[] | [min, max] | Uncontrolled initial values |
| onChange ✱ | (v: number[]) => void | — | Called on every change |
| minDistance | number | 0 | Min gap between adjacent thumbs |
| ariaLabels | string[] | — | ARIA label per thumb |
CircularSlider
SVG radial knob spanning a 270° arc. Mouse, touch, and keyboard supported:
← → ↑ ↓ Home End PageUp PageDown.
import { CircularSlider } from "react-js-multi-range-sliders";
<CircularSlider
min={0}
max={100}
defaultValue={65}
onChange={(v) => console.log(v)}
size={160}
strokeWidth={14}
showLabel
formatLabel={(v) => `${v}%`}
theme="dark"
/>| Prop | Type | Default | Description |
|---|---|---|---|
| min ✱ | number | — | Minimum bound |
| max ✱ | number | — | Maximum bound |
| onChange ✱ | (v: number) => void | — | Called on every change |
| value | number | — | Controlled value |
| defaultValue | number | — | Uncontrolled initial value |
| size | number | 160 | Diameter in px |
| strokeWidth | number | 12 | Track and progress stroke width |
| trackColor | string | — | Background arc colour |
| progressColor | string | — | Progress arc colour |
| thumbColor | string | — | Draggable circle fill |
| showLabel | boolean | true | Centre value label |
| formatLabel | (v: number) => string | String | Custom label text |
| step | number | 1 | Step interval |
| disabled | boolean | false | Read-only state |
| theme | Theme | "default" | Visual theme |
| ariaLabel | string | "Circular slider" | ARIA label |
| onChangeStart | (v: number) => void | — | Fires on pointer-down |
| onChangeComplete | (v: number) => void | — | Fires on pointer-up |
Scale Slider — ruler, labels & subSteps
RangeSlider and SingleSlider both support an optional ruler (tick marks) and an
evenly-spaced label row below the track.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| ruler | boolean | true when labels set | Render tick marks below the track |
| labels | string[] | — | Array of label strings placed at evenly-spaced positions |
| subSteps | boolean \| number | false | Minor ticks between each pair of major ticks. true = 3 minor ticks (4 sub-divisions); any number uses that count explicitly |
Examples
1. Simple range with auto ruler
<RangeSlider
min={0} max={100}
defaultValue={{ min: 25, max: 75 }}
onChange={setRange}
ruler
showLabels
/>The ruler prop auto-generates up to 20 evenly-spaced major ticks based on
Math.min(20, (max - min) / step).
2. Week-day range with named labels
const DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
<RangeSlider
min={0} max={6} step={1}
defaultValue={{ min: 1, max: 5 }}
onChange={setWeekRange}
labels={DAYS}
formatLabel={(v) => DAYS[v]} // tooltip and value display
showTooltip
showLabels={false} // hide redundant number labels
/>labels sets both the ruler tick count and the text shown below each tick.
Pair it with formatLabel to customise the tooltip / current-value text.
3. Date range with month markers
const MONTHS = ["Jan","Feb","Mar","Apr","May","Jun",
"Jul","Aug","Sep","Oct","Nov","Dec"];
<RangeSlider
min={0} max={365}
defaultValue={{ min: 22, max: 364 }}
onChange={setDateRange}
labels={MONTHS}
formatLabel={(v) => {
const date = new Date(2022, 0, v + 1);
return date.toLocaleDateString("en-GB", { day:"numeric", month:"short" });
}}
showTooltip
showLabels={false}
/>4. Time range with sub-step minor ticks
const HOURS = Array.from({ length: 13 }, (_, i) =>
String(i).padStart(2, "0") + ":00"
);
const fmtTime = (v: number) => {
const h = Math.floor(v / 60);
const m = v % 60;
return `${String(h).padStart(2,"0")}:${String(m).padStart(2,"0")}`;
};
<RangeSlider
min={0} max={719} step={1}
defaultValue={{ min: 619, max: 719 }}
onChange={setTimeRange}
labels={HOURS}
subSteps={true} // 3 minor ticks per section = 15-min intervals
formatLabel={fmtTime}
showTooltip
showLabels={false}
/>subSteps={true} inserts 3 minor ticks (default) between each pair of major ticks.
Pass a number to use a custom count, e.g. subSteps={1} for half-way ticks.
5. Negative / positive range
<RangeSlider
min={-1} max={1} step={0.1}
defaultValue={{ min: -0.5, max: 0.5 }}
onChange={setRange}
ruler
showLabels
formatLabel={(v) => v.toFixed(1)}
/>The ruler works across negative ranges. step={0.1} → 20 auto major ticks.
6. Step-snapping range
<RangeSlider
min={0} max={100} step={5}
defaultValue={{ min: 30, max: 60 }}
onChange={setRange}
ruler
showLabels
/>The ruler tick count adjusts to the step size automatically:
sections = Math.min(20, (100 - 0) / 5) = 20 sections → 21 ticks at 0, 5, 10 … 100.
Native <input type="range" step="5"> enforces the snapping.
Themes
| theme | Description |
|---|---|
| "default" | Clean light — white thumb, blue accent |
| "material" | Material Design — 2 px track, purple accent |
| "neumorphic" | Soft 3-D shadows on grey background |
| "dark" | Dark background, cyan accent |
<RangeSlider theme="dark" min={0} max={100} onChange={fn} />
<SingleSlider theme="material" min={0} max={100} onChange={fn} />
<VerticalSlider theme="neumorphic" min={0} max={100} onChange={fn} />
<CircularSlider theme="dark" min={0} max={360} onChange={fn} />Custom colours
Colour props override the active theme token:
<RangeSlider
min={0} max={100}
onChange={fn}
trackColor="#f1f5f9"
rangeColor="#f59e0b"
thumbColor="#fffbeb"
labelColor="#78350f"
/>Tooltips
<RangeSlider
min={0} max={500} step={5}
onChange={fn}
showTooltip
formatLabel={(v) => `$${v}`}
/>
<SingleSlider
min={0} max={100}
onChange={fn}
showTooltip
formatLabel={(v) => `${v}%`}
/>Callbacks
<RangeSlider
min={0} max={100}
onChange={(v) => setRange(v)} // every pointer move
onChangeStart={(v) => logStart(v)} // pointer down
onChangeComplete={(v) => save(v)} // pointer up / release
/>TypeScript
All props are fully typed. Import types as needed:
import type {
Theme,
Direction,
RangeValue,
RangeSliderProps,
SingleSliderProps,
VerticalSliderProps,
MultiPointSliderProps,
CircularSliderProps,
} from "react-js-multi-range-sliders";Accessibility
- Every
input[type=range]carriesaria-label,aria-valuemin,aria-valuemax,aria-valuenow,aria-valuetext, andaria-disabled. - Dual-thumb and multi-point sliders are wrapped in
role="group"with a visually hidden label. CircularSliderexposesrole="slider"and responds to standard keyboard keys (← → ↑ ↓ Home End PageUp PageDown).- Vertical sliders set
aria-orientation="vertical"andorient="vertical"(Firefox). - Focus rings use
focus-visibleso they appear only during keyboard navigation.
Tree-shaking
The package ships CommonJS + ESM dual output. Modern bundlers (webpack 5, Vite, Rollup, Next.js) automatically tree-shake unused components.
lib/
index.js / .mjs — full library (CJS / ESM)
RangeSlider.js / .mjs — per-component subpath imports
SingleSlider.js / .mjs
VerticalSlider.js / .mjs
MultiPointSlider.js / .mjs
CircularSlider.js / .mjs
*.d.ts / *.d.mts — TypeScript declarations (auto-generated)"sideEffects": ["**/*.css"] is declared so bundlers preserve the style-injection
code for used components and drop it for unused ones.
Backward compatibility
Code written for v0.2 continues to work unchanged:
// Default import still works
import MultiRangeSlider from "react-js-multi-range-sliders";
<MultiRangeSlider min={0} max={100} onChange={fn} />
<MultiRangeSlider type="single" min={0} max={100} onChange={fn} />Live demo
Try all five components — RangeSlider, SingleSlider, VerticalSlider, MultiPointSlider, CircularSlider — plus the Scale Slider examples, in the browser:
License
MIT © Ankush Chavan
