Skip to content
v2.5.0
English
Русский
Documentation

Slider

Slider is a compound component for selecting one or several numbers from a bounded range.
slider.tsx · 18 lines
import {
  Slider,
  SliderRange,
  SliderThumb,
  SliderThumbIndicator,
  SliderThumbInput,
  SliderTrack,
} from '@vega-ui/react';

<Slider defaultValue={40} style={{ width: '100%' }}>
  <SliderTrack>
    <SliderRange />
Browser support
Widely available
  • Chrome 111
  • Edge 111
  • Firefox 121
  • Safari 15.4
  • Safari on iOS 15.4
Bundle size
Tested in
68 tests

Summary

Slider is a compound component for selecting one or several numbers from a bounded range. Every SliderThumbInput is a native <input type="range"> laid over one SliderTrack, so keyboard, focus, form submission and reset, and assistive technologies behave like the native element. Two thumbs make a range slider.

Imports

import {
  Slider,
  SliderRange,
  SliderThumb,
  SliderThumbInput,
  SliderThumbIndicator,
  SliderTrack,
  useSliderContext,
  useSliderThumbContext,
  type SliderOrientation,
  type SliderProps,
  type SliderRangeProps,
  type SliderSize,
  type SliderThumbProps,
  type SliderThumbInputProps,
  type SliderThumbIndicatorProps,
  type SliderTrackProps,
  type SliderVariant,
} from '@vega-ui/react';

Exported Types

  • SliderProps
  • SliderTrackProps
  • SliderRangeProps
  • SliderThumbProps
  • SliderThumbInputProps
  • SliderThumbIndicatorProps
  • SliderSize
  • SliderVariant
  • SliderOrientation

Minimal Composition

minimal-composition.tsx · 22 lines
import {
  Slider,
  SliderRange,
  SliderThumb,
  SliderThumbIndicator,
  SliderThumbInput,
  SliderTrack,
} from '@vega-ui/react';

export default function MinimalComposition() {
  return (
    <Slider defaultValue={50} style={{ width: '100%', maxWidth: 400 }}>

Required Parts

  • Slider: root, values of all thumbs and shared range settings
  • SliderThumb: one handle per value
  • SliderThumbInput: the native range input of a handle

Optional Parts

  • SliderTrack: the visible rail
  • SliderRange: the filled part of the rail, inside SliderTrack
  • SliderThumbIndicator: the visual handle; without it the thumb is invisible

Composition Order

  1. Slider
  2. SliderTrack > SliderRange
  3. SliderThumb, one per value, index in order from min to max, with SliderThumbInput and SliderThumbIndicator inside

Variants

  • Size: sm, md, lg
  • Variant: primary, secondary
  • Orientation: horizontal or vertical
  • Thumbs: one value or a range of several
  • Value model: controlled or uncontrolled on the root, a number for one thumb or an array for several
  • Step: numeric step or 'any'

Edge Cases

  • Exact values are harder to target with a slider than with typed input.
  • Vertical sliders need an explicit height.
  • Thumbs cannot pass each other; minStepsBetweenThumbs keeps a gap between them.
  • Without value or defaultValue the slider has one thumb in the middle, like <input type="range">.

Common Mistakes

  • Using a slider where exact numeric entry is the primary need.
  • Forgetting visible value feedback for precision-sensitive flows.
  • Leaving thumbs without aria-label or aria-labelledby.
  • Rendering a different number of SliderThumbs than values, or giving two thumbs the same index.

Examples

Basic

Basic: labeled slider with a default value

An uncontrolled slider: defaultValue sets the start and name on the root reaches the thumb input. SliderThumbInput is the native <input type='range'>, so a visible Label points at its id.

slider-with-default-value.tsx · 26 lines
import {
  Label,
  Slider,
  SliderRange,
  SliderThumb,
  SliderThumbIndicator,
  SliderThumbInput,
  SliderTrack,
} from '@vega-ui/react';

export default function SliderWithDefaultValue() {
  return (

Basic: two thumbs for a range

An array defaultValue creates one value per SliderThumb by its index. Each thumb input gets its own name and aria-label, and minStepsBetweenThumbs keeps the shift at least an hour long.

range-slider.tsx · 31 lines
import {
  Fieldset,
  FieldsetLegend,
  Slider,
  SliderRange,
  SliderThumb,
  SliderThumbIndicator,
  SliderThumbInput,
  SliderTrack,
} from '@vega-ui/react';

export default function RangeSlider() {

Controlled/Stateful

Controlled/Stateful: value driving a preview

value with onChangeValue keeps the number in the parent: the readout and the preview swatch follow it while dragging. aria-valuetext announces the value with its unit.

controlled-brightness.tsx · 44 lines
import { useState } from 'react';
import {
  Label,
  Slider,
  SliderRange,
  SliderThumb,
  SliderThumbIndicator,
  SliderThumbInput,
  SliderTrack,
  Text,
} from '@vega-ui/react';

Controlled/Stateful: price filter over a product list

A controlled two-thumb range filters a list as it moves. Prices are formatted once with Intl.NumberFormat and reused for the readout and for aria-valuetext on each thumb.

controlled-range.tsx · 56 lines
import { useState } from 'react';
import {
  Fieldset,
  FieldsetLegend,
  Slider,
  SliderRange,
  SliderThumb,
  SliderThumbIndicator,
  SliderThumbInput,
  SliderTrack,
  Text,
} from '@vega-ui/react';

Controlled/Stateful: adding and removing thumbs

The number of thumbs follows the length of the value array: render one SliderThumb per entry. Thumbs stay ordered and minStepsBetweenThumbs apart, and each gets a label from its position.

any-number-of-thumbs.tsx · 50 lines
import { useState } from 'react';
import {
  Button,
  Slider,
  SliderRange,
  SliderThumb,
  SliderThumbIndicator,
  SliderThumbInput,
  SliderTrack,
  Text,
} from '@vega-ui/react';

Form/Integration

Form/Integration: reading values with FormData

A range with name on the root submits one entry per thumb, so FormData.getAll('age') returns both bounds; a single thumb is read with FormData.get. Form reset restores defaultValue.

native-form-value.tsx · 65 lines
import { FormEvent, useState } from 'react';
import {
  Button,
  Fieldset,
  FieldsetLegend,
  Slider,
  SliderRange,
  SliderThumb,
  SliderThumbIndicator,
  SliderThumbInput,
  SliderTrack,
  Text,

Layout/Overlay

Layout/Overlay: vertical slider

orientation='vertical' grows from the bottom and needs an explicit height. The value is shown under the track, and the keyboard works on both arrow axes.

vertical-slider.tsx · 34 lines
import { useState } from 'react';
import {
  Slider,
  SliderRange,
  SliderThumb,
  SliderThumbIndicator,
  SliderThumbInput,
  SliderTrack,
  Text,
} from '@vega-ui/react';

export default function VerticalSlider() {

Layout/Overlay: volume slider in a popover

An icon-only trigger opens a Popover with a vertical slider. The trigger's aria-label includes the current value, so it is announced without opening the popover, and the icon follows the value.

slider-in-a-popover.tsx · 49 lines
import { useState } from 'react';
import {
  Icon,
  IconButton,
  Popover,
  PopoverContent,
  PopoverTrigger,
  Slider,
  SliderRange,
  SliderThumb,
  SliderThumbIndicator,
  SliderThumbInput,

Error

Error: value above a policy limit

The slider allows the full range, but values above the policy limit are invalid: the thumb input gets aria-invalid and aria-describedby pointing at the error, which disappears once the value is back under the limit.

value-outside-policy-hint.tsx · 48 lines
import { useState } from 'react';
import {
  HelperText,
  Label,
  Slider,
  SliderRange,
  SliderThumb,
  SliderThumbIndicator,
  SliderThumbInput,
  SliderTrack,
  Text,
} from '@vega-ui/react';

Disabled

Disabled: locked value and disabled by a fieldset

disabled on the root locks every thumb; the reason is linked with aria-describedby. A slider inside a Fieldset disabled is disabled natively and gets the disabled look as well — here while automatic brightness is on.

locked-slider.tsx · 59 lines
import { useState } from 'react';
import {
  Fieldset,
  FieldsetLegend,
  HelperText,
  Label,
  Slider,
  SliderRange,
  SliderThumb,
  SliderThumbIndicator,
  SliderThumbInput,
  SliderTrack,

Edge

Edge: free precision with step any

step='any' drops the step grid, so the value can be any fraction between min and max. Arrow keys then move by a hundredth of the range; the readout rounds for display.

free-precision-step.tsx · 33 lines
import { useState } from 'react';
import {
  Label,
  Slider,
  SliderRange,
  SliderThumb,
  SliderThumbIndicator,
  SliderThumbInput,
  SliderTrack,
  Text,
} from '@vega-ui/react';

Edge: value text with units and a negative minimum

A range from −10 to 30 with aria-valuetext, so screen readers say “20 °C” instead of a bare number.

value-text-for-screen-readers.tsx · 25 lines
import {
  Slider,
  SliderRange,
  SliderThumb,
  SliderThumbIndicator,
  SliderThumbInput,
  SliderTrack,
} from '@vega-ui/react';
import { useState } from 'react';

export default function ValueTextForScreenReaders() {
  const [temperature, setTemperature] = useState(20);

Edge: snapping to datalist markers

A <datalist> connected through list makes the native input snap near its values while dragging. The browser does not draw the marks, so draw your own if they must be visible.

markers-from-a-datalist.tsx · 29 lines
import {
  Slider,
  SliderRange,
  SliderThumb,
  SliderThumbIndicator,
  SliderThumbInput,
  SliderTrack,
} from '@vega-ui/react';

export default function MarkersFromADatalist() {
  return (
    <>

Edge: vertical group with a negative range

Five vertical sliders from −12 to +12 dB form an equalizer inside a labelled role='group'. Each thumb is named by its band and announces its gain in dB.

vertical-group-negative-range.tsx · 46 lines
import {
  Slider,
  SliderRange,
  SliderThumb,
  SliderThumbIndicator,
  SliderThumbInput,
  SliderTrack,
  Text,
} from '@vega-ui/react';
import { useState } from 'react';

const bands = ['60 Hz', '230 Hz', '910 Hz', '3.6 kHz', '14 kHz'];

Anatomy

Overview

Slider holds the values of all thumbs and the shared settings (min, max, step, orientation, disabled, name). Each SliderThumb groups a SliderThumbInput, a native <input type="range"> laid over its part of the track between its neighbours, and a SliderThumbIndicator drawn at its value.

Required Parts

Slider

Required. Passes range settings to the thumbs and keeps neighbouring thumbs apart. A vertical slider needs a height from its parent.

SliderThumb

Required. One handle: index picks its entry of the root value and the position variables are set for its parts.

SliderThumbInput

Required, a direct child of SliderThumb. A controlled native range input: focus, keyboard, form value and accessibility come from the browser; its min and max are the neighbours.

Optional Parts

SliderThumbIndicator

The visual handle, a direct child of SliderThumb. It can hold any content: a value bubble, an icon, an image. It lets the pointer through to the native input under it.

SliderTrack

The visible rail. Without it the thumbs still work over an invisible track.

SliderRange

The filled part of the rail: from min to a single thumb, or between the first and the last thumbs.

Composition Order

  1. Slider
  2. SliderTrack > SliderRange
  3. SliderThumb, one per value
    1. SliderThumbInput
    2. SliderThumbIndicator

Valid Composition Patterns

range-slider.tsx · 31 lines
import {
  Fieldset,
  FieldsetLegend,
  Slider,
  SliderRange,
  SliderThumb,
  SliderThumbIndicator,
  SliderThumbInput,
  SliderTrack,
} from '@vega-ui/react';

export default function RangeSlider() {

Invalid Composition Patterns

Root without thumbs

The track renders, but there is nothing to interact with or submit.

Thumbs outside the root

SliderThumb reads its settings from the root and is positioned over the root track.

Input or indicator outside SliderThumb

They read their index from SliderThumb; the hover, active, focus and disabled styles of the indicator follow the input of the same SliderThumb.

Vertical slider without a height

The control renders, but the interaction space collapses.