Skip to content

Range Slider

Edit on GitHub
@volvo-cars/css v2.3.0@volvo-cars/react-forms v2.0.0

The RangeSlider component lets users select values within a defined range by dragging a thumb along a track. For selecting a min/max range, use the DoubleRangeSlider variant with two thumbs.

A simple range slider with controlled value.

import { RangeSlider } from '@volvo-cars/react-forms';
import { useState } from 'react';
export function RangeSliderBasic() {
const [value, setValue] = useState(50);
return (
<div className="my-24">
<RangeSlider
value={value}
onChange={(e) => setValue(Number(e.target.value))}
/>
</div>
);
}

Enable the tooltip prop to show the current value while dragging.

50
import { RangeSlider } from '@volvo-cars/react-forms';
import { useState } from 'react';
export function RangeSliderWithTooltip() {
const [value, setValue] = useState(50);
return (
<div className="my-24">
<RangeSlider
tooltip
value={value}
onChange={(e) => setValue(Number(e.target.value))}
/>
</div>
);
}

Pair the slider with a label and <output> element to display the current value prominently.

20,000 mi

import { RangeSlider } from '@volvo-cars/react-forms';
import { useState } from 'react';
export function RangeSliderWithLabel() {
const [value, setValue] = useState(20000);
return (
<div className="stack-8">
<label htmlFor="mileage-range">Yearly mileage</label>
<p>
<output className="font-20 font-medium" htmlFor="mileage-range">
{value.toLocaleString('en-GB')}
<span className="micro font-medium"> mi</span>
</output>
</p>
<RangeSlider
className="mt-32"
id="mileage-range"
value={value}
min={10000}
max={30000}
onChange={(e) => setValue(Number(e.target.value))}
/>
</div>
);
}

The DoubleRangeSlider renders two thumbs for selecting a min/max range. It wraps two native <input type="range"> elements inside a role="group" container.

import { DoubleRangeSlider } from '@volvo-cars/react-forms';
import { useState } from 'react';
export function DoubleRangeSliderBasic() {
const [value, setValue] = useState<[number, number]>([25, 75]);
return (
<div className="my-24">
<DoubleRangeSlider
value={value}
onValueChange={setValue}
minLabel="Minimum value"
maxLabel="Maximum value"
aria-label="Range"
/>
</div>
);
}

The value and defaultValue props accept a [number, number] tuple. Use onValueChange to receive the updated [min, max] pair directly, or onChange for the native input event.

2575
import { DoubleRangeSlider } from '@volvo-cars/react-forms';
import { useState } from 'react';
export function DoubleRangeSliderWithTooltip() {
const [value, setValue] = useState<[number, number]>([25, 75]);
return (
<div className="my-24">
<DoubleRangeSlider
tooltip
value={value}
onValueChange={setValue}
minLabel="Minimum value"
maxLabel="Maximum value"
aria-label="Range with tooltip"
/>
</div>
);
}

10,00025,000 mi

import { DoubleRangeSlider } from '@volvo-cars/react-forms';
import { useState } from 'react';
export function DoubleRangeSliderWithLabel() {
const [value, setValue] = useState<[number, number]>([10000, 25000]);
return (
<div className="stack-8">
<label id="mileage-label" htmlFor="mileage-range-min">
Yearly mileage
</label>
<p>
<output
className="font-20 font-medium"
htmlFor="mileage-range-min mileage-range-max"
>
{value[0].toLocaleString('en-GB')}–{value[1].toLocaleString('en-GB')}
<span className="micro font-medium"> mi</span>
</output>
</p>
<DoubleRangeSlider
className="mt-32"
id="mileage-range"
aria-labelledby="mileage-label"
min={5000}
max={30000}
step={500}
minLabel="Minimum mileage"
maxLabel="Maximum mileage"
value={value}
onValueChange={setValue}
/>
</div>
);
}

Use aria-labelledby on the DoubleRangeSlider to reference a visible <label>. Customise minLabel and maxLabel to give each thumb a meaningful accessible name (defaults are “Minimum value” and “Maximum value”).

The RangeSlider can be used in two modes:

  • Controlled: Set both value and onChange props to manage the value externally
  • Uncontrolled: Use defaultValue for initial value, let the component manage state internally
// Controlled
<RangeSlider value={value} onChange={(e) => setValue(Number(e.target.value))} />
// Uncontrolled
<RangeSlider defaultValue={50} />

The same pattern applies to DoubleRangeSlider, where value and defaultValue accept a [number, number] tuple.

  • Use a <label> element linked via htmlFor matching the slider’s id
  • Use <output> element with htmlFor to announce value changes to screen readers
  • Arrow keys adjust the value when the slider is focused
  • The thumb has a minimum touch target of 40×40 pixels

For the DoubleRangeSlider:

  • The component renders a role="group" container — label it with aria-labelledby or aria-label
  • Each thumb has its own accessible name via minLabel and maxLabel — always customise these to match the context (e.g. “Minimum price”, “Maximum price”)
  • When tooltip is enabled, <output> elements with aria-live="polite" announce value changes

See the full accessibility documentation for WCAG 2.2 criteria details.

KeyAction
Arrow RightIncrease value by step
Arrow LeftDecrease value by step
Arrow UpIncrease value by step
Arrow DownDecrease value by step
HomeSet to minimum value
EndSet to maximum value
TabMove focus between thumbs (DoubleRangeSlider)
Prop Type Required Default
name string - -
The name of the input to use when submitting the form.
form string - -
Id of a form element that this input should be associated with. Defaults to the containing form element.
onFocus FocusEventHandler<HTMLInputElement> - -
onBlur FocusEventHandler<HTMLInputElement> - -
onKeyDown KeyboardEventHandler<HTMLInputElement> - -
onKeyUp KeyboardEventHandler<HTMLInputElement> - -
min number - 0
The minimum value for the input.
max number - 100
The maximum value for the input.
step number - -
The step interval between values.
list string - -
Id of a `<datalist>` element with a list of predefined values to suggest to the user.
defaultValue number - -
Default value of an uncontrolled input.
tooltip boolean - false
Provide a tooltip while sliding, indicating the current value.
value number - -
Value of the input. Makes the input controlled.
onChange ChangeEventHandler<HTMLInputElement, Element> - -
Fires immediately when the input’s value is changed by the user (for example, it fires on every keystroke).
hidden boolean - -
id string - -
title string - -
dir string - -
lang string - -
slot string - -
translate "yes" | "no" - -
className string - -
style CSSProperties - -
tabIndex number - -
onPointerDown PointerEventHandler<Element> - -
onPointerEnter PointerEventHandler<Element> - -
onPointerLeave PointerEventHandler<Element> - -
onPointerMove PointerEventHandler<Element> - -
onPointerUp PointerEventHandler<Element> - -
Prop Type Required Default
name string - -
The name of the input to use when submitting the form.
form string - -
Id of a form element that this input should be associated with. Defaults to the containing form element.
onFocus FocusEventHandler<HTMLInputElement> - -
onBlur FocusEventHandler<HTMLInputElement> - -
onKeyDown KeyboardEventHandler<HTMLInputElement> - -
onKeyUp KeyboardEventHandler<HTMLInputElement> - -
min number - 0
The minimum value for the input.
max number - 100
The maximum value for the input.
step number - 1
The step interval between values.
list string - -
Id of a `<datalist>` element with a list of predefined values to suggest to the user.
defaultValue [number, number] - -
Default value of an uncontrolled input.
precision number - -
Number of decimal places to round values to.
tooltip boolean - false
Provide a tooltip while sliding, indicating the current value.
value [number, number] - -
Value of the input. Makes the input controlled.
onChange ChangeEventHandler<HTMLInputElement, Element> - -
Native change event handler, passed through as-is from whichever input fired. Use this when you need access to the underlying DOM event (e.g. to inspect `event.target.name` or integrate with form libraries).
onValueChange ((values: [number, number]) => void) - -
Fires immediately when either slider's value changes. Provides the full numeric pair `[min, max]` as the primary payload so consumers can keep the range value in state without parsing native events.
minLabel string - Minimum value
Accessible label for the minimum value input.
maxLabel string - Maximum value
Accessible label for the maximum value input.
aria-labelledby string - -
ID of an element that labels the slider group.
aria-label string - -
Accessible label for the slider group.
hidden boolean - -
id string - -
title string - -
dir string - -
lang string - -
slot string - -
translate "yes" | "no" - -
className string - -
style CSSProperties - -
tabIndex number - -
onPointerDown PointerEventHandler<Element> - -
onPointerEnter PointerEventHandler<Element> - -
onPointerLeave PointerEventHandler<Element> - -
onPointerMove PointerEventHandler<Element> - -
onPointerUp PointerEventHandler<Element> - -