Mosmox UI
Current version
GitHubnpmjs.com
Forms & Inputs

Slider

An accessible, token-based range control with numeric, centered, predefined-option, and color-range modes.

Slider is a single-value range control for settings such as volume, opacity, zoom, temperature, crop size, discrete presets, and color selection. It wraps a native input type="range", so keyboard and screen-reader behavior follow the platform while the visual track uses the framework tokens.

The component supports controlled and uncontrolled state, custom units, three visual sizes, disabled state, formatted values, initialization deltas, commit callbacks, centered negative/positive ranges, finite option lists, and interpolated color ranges.

Basic usage

<Slider aria-label="Volume" label="Volume" defaultValue={42} />

Use value and onChange for controlled state. The callback receives the numeric value and the native change event.

const [volume, setVolume] = useState(42);

<Slider
  label="Volume"
  max={100}
  value={volume}
  onChange={setVolume}
  valueFormatter={(value) => `${value}%`}
/>

Ranges, steps, and sizes

min, max, and step define the range. size supports exactly xs (extra small), sm (small), and default. There is no large Slider size.

<Slider size="xs" aria-label="Opacity" max={1} step={0.05} defaultValue={0.65} valueFormatter={(value) => `${Math.round(value * 100)}%`} />
<Slider size="sm" label="Temperature" min={-10} max={40} step={1} defaultValue={21} valueFormatter={(value) => `${value}°C`} />
<Slider size="default" label="Zoom" min={25} max={200} step={5} defaultValue={100} valueFormatter={(value) => `${value}%`} />

Accessibility and states

Provide aria-label when no visible string label is supplied. The native range control supports Arrow keys, Page Up/Down, Home, and End. Use disabled to prevent changes while preserving the current value.

showValue displays an <output> next to the label. Use valueFormatter to add units or a localized representation.

In uncontrolled mode, the track fill and current value update internally while the component is dragged. In controlled mode, the parent must update value from onChange to keep the thumb and fill synchronized. Values are clamped to the active range.

Initial value, delta, and commit handling

Use showInitialValue to keep the value captured at initialization visible and showDelta to show the signed difference from that value. The initial value is value when controlled, otherwise defaultValue, and it remains stable during the component lifetime.

Use onChangeEnd when work should happen only after the user finishes dragging or keyboard adjustment. It fires after pointer release, after a keyboard interaction, or when the slider loses focus. It receives the final numeric value and the native change event.

<Slider
  label="Temperature"
  min={-10}
  max={40}
  defaultValue={21}
  showInitialValue
  showDelta
  valueFormatter={(value) => `${value}°C`}
  onChangeEnd={(value) => saveTemperature(value)}
/>

Centered ranges

Set mode="centered" to make zero the neutral origin. Use a negative min and a positive max; the filled track then grows from zero toward the negative or positive side.

const [balance, setBalance] = useState(0);

<Slider
  label="Balance"
  mode="centered"
  min={-100}
  max={100}
  value={balance}
  onChange={setBalance}
  valueFormatter={(value) => value === 0 ? 'Center' : `${value > 0 ? '+' : ''}${value}`}
/>

Centered mode supports any negative minimum and positive maximum, including asymmetric ranges such as min={-30} and max={70}. If the range does not cross zero, the component falls back to the normal linear fill.

min, max, and step still control the numeric values in centered mode. For example, min={-1}, max={1}, and step={0.01} creates hundredths around zero.

Predefined options

Use mode="options" when the slider should move through a finite list instead of a numeric range. The input value is the zero-based option index. Options can be numbers, strings, or objects with value, label, and an optional color. Use optionFormatter for custom display and onOptionChange to receive the selected option object.

<Slider
  label="Density"
  mode="options"
  options={[
    { value: 'compact', label: 'Compact' },
    { value: 'comfortable', label: 'Comfortable' },
    { value: 'spacious', label: 'Spacious' },
  ]}
  defaultValue={1}
  onOptionChange={(option) => setDensity(option.value)}
/>

The slider position in options mode is the zero-based array index. Therefore defaultValue={0} selects the first option, defaultValue={1} selects the second, and so on. onChange receives the index, while onOptionChange receives the normalized option object and its index. When an option has a color, the shared color swatch can display it.

Color ranges

Pass colorRange to create a slider that interpolates between two colors. The thumb and a color swatch next to the selected value show the current color. showColorSwatch={false} hides the swatch while preserving the color track.

colorRange.from and colorRange.to accept CSS color strings. Hex colors are interpolated directly; other CSS color formats use the browser's color-mix() function. The range is visual and does not change the numeric value or onChange contract.

<Slider
  label="Accent color"
  min={0}
  max={100}
  defaultValue={65}
  colorRange={{ from: '#2563eb', to: '#f97316' }}
  valueFormatter={(value) => `${value}%`}
/>

API reference

PropertyTypeDefaultDescription
valuenumber—Controlled value.
defaultValuenumber0Initial uncontrolled value.
minnumber0Minimum value.
maxnumber100Maximum value.
stepnumber1Keyboard and pointer increment.
mode'linear' | 'centered' | 'options'linearLinear fill, fill from zero in both directions, or a finite options list.
optionsArray<SliderOption | string | number>—Finite values used by mode="options".
optionFormatter(option, index) => ReactNode—Custom display for the selected option.
onOptionChange(option, index, event) => void—Called when an options slider selects a new option.
colorRange{ from: string; to: string }—Creates a color interpolation track and selected-color thumb.
showColorSwatchbooleantrueShows the selected color square when a color range or option color is present.
onChange(value, event) => void—Called with the numeric value and native change event.
onChangeEnd(value, event) => void—Called once after a completed pointer, keyboard, or focus interaction.
labelReactNode—Visible label.
aria-labelstring—Accessible name when the visible label is not enough.
showValuebooleantrueShows the formatted value.
showInitialValuebooleanfalseShows the initialization value beside the current value.
showDeltabooleanfalseShows the signed difference from the initialization value.
valueFormatter(value) => ReactNodeidentityFormats the displayed value.
size'xs' | 'sm' | 'default'defaultTrack and thumb size. xs is extra small, sm is small.
disabledbooleanfalseDisables interaction.
idstringgeneratedID shared by label, output, and input.
classNamestring—Additional root class.

Exported helper types

TypeShapeDescription
SliderSize'xs' | 'sm' | 'default'Supported visual sizes.
SliderMode'linear' | 'centered' | 'options'Slider operating mode.
SliderOption{ value: string | number; label?: ReactNode; color?: string }Normalized predefined option.
SliderColorRange{ from: string; to: string }Start and end colors for interpolation.

Design tokens

The track, fill, thumb, focus ring, surface, typography, and spacing use shared tokens such as --mm-color-primary, --mm-color-control-filled, --mm-color-surface, --mm-color-focus, --mm-color-text, and --mm-color-muted-text. Scope token overrides to a theme container to create a custom slider without changing the component API.