Dots

Range Histogram

A two-thumb range slider drawn over a histogram of the values it filters.

$50 – $350
"use client"

import { RangeHistogram } from "@/components/ui/range-histogram"

The RangeHistogram component is a two-thumb range slider over a histogram of the values it filters, such as transaction amounts. Bars inside the selected range are emphasized, so people see how many payments a range keeps before they let go. Use it in a filter sheet when the shape of the data helps pick a range. Pick a plain Slider when the values are spread evenly or the shape of the data doesn't matter, or two amount inputs when people already know the exact bounds.

Installation

pnpm dlx shadcn@latest add @dots/range-histogram

Usage

import { RangeHistogram } from "@/components/ui/range-histogram"
<RangeHistogram
  label="Amount"
  data={[8, 14, 26, 44, 62, 78, 100, 86, 70, 58, 46, 40]}
  minValue={0}
  maxValue={600}
  defaultValue={[50, 350]}
  formatOptions={{ style: "currency", currency: "USD" }}
/>

Features

  • Two thumbs on the React Aria Slider, with keyboard, touch and screen reader support
  • A decorative histogram above the track, with bars inside the range emphasized as the thumbs move
  • The selected range shown as formatted text next to the label, using formatOptions
  • Works controlled or uncontrolled, with onChangeEnd for work that should wait until the thumb is let go
  • Bad counts, such as negative or NaN, draw as empty bars instead of breaking the chart

Data

data holds how many values fall in each bucket. The buckets are spread evenly from minValue to maxValue, so 24 counts for 0 to 600 make $25 buckets. A bar is emphasized when its bucket overlaps the selected range. Counts are scaled to the highest one, so they don't need to be normalized.

// 24 buckets of $25 between $0 and $600.
<RangeHistogram data={counts} minValue={0} maxValue={600} step={25} />

Controlled

Control the range with value and onChange. Use onChangeEnd for work that should run once the thumb is let go, such as fetching results or announcing a match count.

$100 – $300

540 of 776 payments

"use client"

import * as React from "react"

Without output

Set showOutput={false} to hide the range text, such as when the filter's results already show the range. Without a visible label, pass aria-label.

"use client"

import { RangeHistogram } from "@/components/ui/range-histogram"

Disabled

Use isDisabled while the data loads or the filter doesn't apply.

$0 – $600
"use client"

import { RangeHistogram } from "@/components/ui/range-histogram"

Accessibility

RangeHistogram is built on the React Aria Slider. Each thumb is a native range input: arrow keys move by step, Page Up and Page Down move by a larger step, and Home and End jump to the ends. The histogram is decorative and hidden from assistive tech.

Label the slider

Pass a visible label, which also names the slider. Without one, pass aria-label.

<RangeHistogram aria-label="Amount" data={counts} />

Name the thumbs

The thumbs are named "Minimum" and "Maximum" by default. Pass thumbLabels with words that fit the data and your interface language.

<RangeHistogram
  label="Amount"
  data={counts}
  thumbLabels={["Lowest amount", "Highest amount"]}
/>

Format values

Pass formatOptions so each thumb reads as a value, such as "$50", not a bare number.

Announce results

Assistive tech doesn't see the bars. If the range changes a count of results, show it as text and announce it from onChangeEnd, so it is read once and not on every step of a drag.

API Reference

RangeHistogram

Accepts the React Aria Slider props for a number[] value except orientation, children and className.

PropTypeDefaultDescription
datanumber[]-How many values fall in each bucket, spread evenly from minValue to maxValue.
labelReactNode-The visible label. It also names the slider. Without it, pass aria-label.
thumbLabels[string, string]["Minimum", "Maximum"]The accessible names of the two thumbs.
showOutputbooleantrueShow the selected range as text next to the label.
minValuenumber0The lowest value.
maxValuenumber100The highest value.
stepnumber1The step between values.
valuenumber[]-The selected range (controlled).
defaultValuenumber[][minValue, maxValue]The selected range (uncontrolled).
onChange(value: number[]) => void-Called as the thumbs move.
onChangeEnd(value: number[]) => void-Called when a thumb is let go.
formatOptionsIntl.NumberFormatOptions-Formats the output and the values read by assistive tech.
isDisabledboolean-Disables the slider.
classNamestring-Additional classes to apply to the root element.