Skip to content
WANCRAFT

F.83Forms & Inputs

Dual Range Slider

A two-thumb range slider with an optional histogram, value bubbles, a minimum gap and full keyboard support.

NewReactTailwindintermediate

Overview

Dual Range Slider picks a span — a price band, a date window, a size range — with two thumbs on one track. An optional histogram above the track shows how values are distributed and lights up the bars inside the selection. Pressing anywhere on the track moves the nearest thumb and keeps dragging it with pointer capture; thumbs never cross, and you choose whether they stop at a minimum distance or push each other along. Each thumb is its own role="slider" with a formatted aria-valuetext (currency, units), and two hidden inputs submit the range with a form.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/dual-range-slider.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/dual-range-slider.json

Method 02 · Copy and paste

  1. 1. No extra dependencies — React and Tailwind CSS only.
  2. 2. Copy the source from the Code tab into components/ui/dual-range-slider.tsx.
  3. 3. Import it and adjust props.
Dependencies
None
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { DualRangeSlider } from "@/components/ui/dual-range-slider";

export function DualRangeSliderExample() {
  // One count per bucket, evenly spread between min and max.
  const histogram = [2, 5, 9, 14, 22, 30, 34, 29, 24, 18, 13, 9, 6, 4, 3, 2];

  return (
    <DualRangeSlider
      label="Price"
      max={1000}
      step={10}
      defaultValue={[200, 640]}
      histogram={histogram}
      formatValue={(v) => `$${v}`}
    />
  );
}

03Props

PropTypeDefaultDescription
labelstring—Required. Visible label; thumbs are named “<label>, minimum” and “<label>, maximum”.
value / defaultValue[number, number]— / 25%–75% of the rangeControlled range, or the initial range. Values are ordered, snapped to step and kept minDistance apart.
onValueChange(value: [number, number]) => void—Called on every change of either end.
onValueCommit(value: [number, number]) => void—Called when a drag ends and after keyboard changes.
min / max / stepnumber0 / 100 / 1Range and step.
minDistancenumber0Smallest allowed gap between the thumbs.
collision"block" | "push""block"Thumbs stop at minDistance from each other, or push the other thumb along. They never cross or swap.
histogramnumber[]—Counts per bucket, spread evenly from min to max. Bars whose centre lies inside the range are highlighted.
histogramHeightnumber48Histogram height in px.
formatValue(value: number) => string—Formats bubbles, the readout, bounds and aria-valuetext.
bubble"always" | "active" | "off""active"Value bubbles above the thumbs; they merge into one when they would overlap.
showBoundsbooleantrueShow the formatted min and max under the track.
hideLabelbooleanfalseVisually hide the label row (kept for screen readers).
accentstring"#FF7448"Fill, highlighted bars, thumb centre and focus ring.
disabledbooleanfalseDisable all input.
namestring—Adds two hidden inputs with this name (min first): formData.getAll(name) → [min, max].
...propsHTMLAttributes<HTMLDivElement>—Forwarded to the root role="group" element.

04Features

  • 01Optional histogram that highlights the buckets inside the selection
  • 02Press anywhere on the track to move the nearest thumb, then keep dragging (pointer capture)
  • 03Minimum distance with block or push collision; thumbs never cross
  • 04Value bubbles that merge into a single “min – max” bubble when close
  • 05Custom formatter for currency or units, shared with aria-valuetext
  • 06Controlled or uncontrolled, with hidden inputs for native forms
  • 07Re-renders only when a stepped value changes, not on every pointer move
motion · tweeninput · draginput · pointerinput · keyboardinput · focus

05Accessibility

  • Two focusable role="slider" thumbs inside a labelled role="group", each with its own name, aria-valuenow and formatted aria-valuetext.
  • In block mode each thumb's aria-valuemin / aria-valuemax follow the other thumb, as in the WAI-ARIA multi-thumb slider pattern.
  • Arrow keys step, Shift + arrow and Page Up / Page Down move a tenth of the range, Home / End jump to the thumb's limits.
  • Bubbles appear on keyboard focus as well as while dragging; the visual readout and histogram are aria-hidden because the thumbs carry the values.
  • Visible focus ring in the accent colour; bubble and thumb transitions are disabled with prefers-reduced-motion.

06Limitations

  • The histogram is decorative. If the distribution matters, describe it in nearby text as well.
  • Horizontal only; vertical sliders are not supported.