Slider

Chooses a number, or a range with one thumb per value, along a track.

import { Slider } from "@usefragments/ui";Source

Preview

<div style={{ width: '300px' }}>
  <Slider label="Volume" defaultValue={50} />
</div>

Usage

Use when

  • Any value along a range, where roughly right is good enough
  • Volume, brightness, opacity
  • Price and range filters
  • The effect is visible as the user drags

Avoid when

  • An exact number matters (use Input type="number")
  • A handful of fixed choices (use RadioGroup or Select)
  • On or off (use Switch)
  • A value the user cannot change here (show it as text, or set readOnly when it sits among editable fields)

Implementation notes

  • Label what the track controls
  • Turn on showValue whenever the number matters, and format it with Intl options instead of a string suffix
  • Set min and max to the real limits, not 0-100 by habit
  • Pick a step the user can actually land on
  • Give each thumb of a range its own name with getAriaLabel

Installation

pnpm add @usefragments/ui

Import styles once from @usefragments/ui/styles. See the installation guide for framework and theme setup.

Examples

With Value Display

showValue prints the number opposite the label.

<div style={{ width: '300px' }}>
  <Slider
    label="Opacity"
    defaultValue={0.75}
    max={1}
    step={0.05}

Custom Range

min, max and step bound the track to real-world units.

<div style={{ width: '300px', display: 'flex', flexDirection: 'column', gap: '16px' }}>
  <Slider
    label="Temperature"
    min={60}
    max={80}
    defaultValue={72}

Range

Two thumbs choose a span; each thumb has its own name.

<div style={{ width: '300px' }}>
  <Slider
    label="Price"
    defaultValue={[20, 80]}
    getAriaLabel={(index) => (index === 0 ? 'Minimum price' : 'Maximum price')}
    format={{ style: 'currency', currency: 'USD', maximumFractionDigits: 0 }}

Controlled

The parent owns the value and can react to every move.

<ControlledSliderDemo />

Drag Value Bubble

The number floats above the thumb only while dragging.

<div style={{ width: '300px' }}>
  <Slider label="Mix" defaultValue={40} showValueOnDrag />
</div>

With Helper Text

A line below the track explains the trade-off.

<div style={{ width: '300px' }}>
  <Slider
    label="Quality"
    defaultValue={80}
    showValue
    helperText="Higher quality increases file size"

Invalid

The track takes the danger edge; the message has an icon and words.

<div style={{ width: '300px' }}>
  <Slider
    label="Budget"
    defaultValue={90}
    showValue
    invalid

Read-only

A dashed track; the thumb does not move.

<div style={{ width: '300px' }}>
  <Slider label="Seats" defaultValue={12} max={20} showValue readOnly />
</div>

Disabled

Dimmed; the thumb will not move.

<div style={{ width: '300px' }}>
  <Slider
    label="Locked Setting"
    defaultValue={30}
    showValue
    disabled

API reference

Component props
Prop
Type
Default
Description
aria-describedbystring | undefinedNot setAccessible described-by relationship
aria-labelstring | undefinedNot setAccessible label when the visible label is omitted
aria-labelledbystring | undefinedNot setAccessible labelled-by relationship
defaultValueValue | undefinedNot setDefault value for uncontrolled usage
disabledboolean | undefinedfalseDisable the slider
errorMessageReact.ReactNodeNot setMessage shown under the slider while invalid is true
formstring | undefinedNot setID of the form that owns the hidden input
formatIntl.NumberFormatOptions | undefinedNot setNumber formatting for the shown value, the bubble and the announced value
getAriaLabel((index: number) => string) | undefinedNot setAccessible name for each thumb of a range, by index: (index: number) => string
helperTextstring | undefinedNot setHelper text shown below the slider
invalidboolean | undefinedfalseWhether the value fails validation. The track takes the danger edge and errorMessage shows under it with an icon.
labelstring | undefinedNot setVisible label text. A slider needs a label, aria-label or aria-labelledby; there is no fallback name.
largeStepnumber | undefinedNot setLarger step for Page Up/Page Down and shift-arrow keys
localeIntl.LocalesArgumentNot setLocale for format; defaults to the runtime locale
maxnumber | undefined100Maximum value
minnumber | undefined0Minimum value
namestring | undefinedNot setName of the hidden input for form submission
onValueChange((value: SliderOutput<Value>) => void) | undefinedNot setCalled with the new value while the user moves a thumb: (value: number | number[]) => void
onValueCommitted((value: SliderOutput<Value>) => void) | undefinedNot setCalled with the value when the user finishes an interaction
readOnlyboolean | undefinedfalseShow the value without letting the user change it
showValueboolean | undefinedfalseShow the formatted value beside the label
showValueOnDragboolean | undefinedfalseShow the formatted value in a bubble above the thumb while dragging
size"xs" | "sm" | "md" | "lg" | undefinedxssmmdlg"md"Height of the control strip, so a slider lines up with fields of the same size. The track and thumb keep one size.
stepnumber | undefined1Step interval
thumbAlignment"center" | "edge" | "edge-client-only" | undefinedcenteredgeedge-client-onlyNot setHow the thumb aligns at the minimum and maximum edges
valueValue | undefinedNot setControlled value: a number, or an array for a range (one thumb per value)
idstring | undefinedNot setID of the slider root

Accessibility

  • The visible label names every thumb; a range names each thumb with getAriaLabel
  • Arrow keys move the thumb; Page Up and Page Down move by largeStep
  • The formatted value is announced
  • An invalid slider shows an icon and words, wired to the thumb with aria-describedby
  • A read-only slider says so with aria-readonly
  • InputalternativeUse Input for precise numeric entry
  • ProgresssiblingSimilar visual, but Progress is read-only

Next steps