Skip to content
LuoForge/Tungsten
Components/Slider·

Slider

since v8.6.0

Range-input primitive for selecting a value (single thumb) or a bounded

range (two thumbs). Wraps @radix-ui/react-slider (per

docs/adr/0001-headless-primitive-baseline.md) — Radix owns role="slider",

aria-valuemin/max/now, keyboard (Arrows, Home/End, Page Up/Down), RTL,

and thumb collision; we own the public API shape and the visual treatment.

The mode is inferred from the value shape: a number is single-thumb, a

[number, number] tuple is a range. onValueChange and onValueCommit

both echo that shape.

Out-of-range values are clamped and reversed range tuples are sorted before

reaching Radix, so aria-valuenow/min/max always stay coherent. Pass

aria-valuetext (a string, or a (value) => string formatter for per-thumb

text) to expose formatted values; it is routed to the thumb(s), not the root.

Like any uncontrolled input, defaultValue is read once at mount: changing

its shape (number ↔ tuple) afterward will not re-seed the thumbs. Use the

controlled value prop if the mode can change at runtime.

Install

Add the package and import the component.

pnpm add @hey-mike/tungsten
pnpm add @hey-mike/tungsten
import { Slider } from '@hey-mike/tungsten';
import { Slider } from '@hey-mike/tungsten';

Preview

Same fixtures used by the visual-regression suite.

Usage

apps/docs/app/snapshots/slider/page.tsx

'use client';

import { Slider } from '@hey-mike/tungsten';
import { VariantGrid } from '../_components/VariantGrid';

export default function SliderSnapshot() {
  return (
    <VariantGrid
      title="Slider"
      variants={[
        {
          label: 'single',
          node: (
            <div className="w-64">
              <Slider label="Volume" defaultValue={40} />
            </div>
          ),
        },
        {
          label: 'range',
          node: (
            <div className="w-64">
              <Slider label="Price" defaultValue={[25, 75]} />
            </div>
          ),
        },
        {
          label: 'stepped',
          node: (
            <div className="w-64">
              <Slider label="Zoom" defaultValue={60} step={10} />
            </div>
          ),
        },
        {
          label: 'disabled',
          node: (
            <div className="w-64">
              <Slider label="Locked" defaultValue={50} disabled />
            </div>
          ),
        },
      ]}
    />
  );
}
'use client';

import { Slider } from '@hey-mike/tungsten';
import { VariantGrid } from '../_components/VariantGrid';

export default function SliderSnapshot() {
  return (
    <VariantGrid
      title="Slider"
      variants={[
        {
          label: 'single',
          node: (
            <div className="w-64">
              <Slider label="Volume" defaultValue={40} />
            </div>
          ),
        },
        {
          label: 'range',
          node: (
            <div className="w-64">
              <Slider label="Price" defaultValue={[25, 75]} />
            </div>
          ),
        },
        {
          label: 'stepped',
          node: (
            <div className="w-64">
              <Slider label="Zoom" defaultValue={60} step={10} />
            </div>
          ),
        },
        {
          label: 'disabled',
          node: (
            <div className="w-64">
              <Slider label="Locked" defaultValue={50} disabled />
            </div>
          ),
        },
      ]}
    />
  );
}

Props

Surface specific to <Slider />.

PropTypeDefaultDescription
valueSliderValueCurrent value. A number renders one thumb; a [min, max] tuple renders a range.
defaultValueSliderValueInitial value in uncontrolled mode. Defaults to a single thumb at min.
onValueChange((value: SliderValue) => void)Fires with the next value, matching the shape of value/defaultValue.
onValueCommit((value: SliderValue) => void)Fires when interaction settles, matching the shape of value/defaultValue.
aria-valuetextstring | ((value: number) => string)Accessible text for the value, routed to each thumb (not the root). Pass a string for a single shared label, or a function to format per thumb value (e.g. (v) => $${v}``).
label*stringAccessible name applied to the thumb(s). Range thumbs get "(minimum)"/"(maximum)" suffixes.
minnumber0
maxnumber100
stepnumber1

Used in recipes

Compositions from the /recipes reference that use this component.

Source