Skip to content

Slider

Picks a value by dragging along a range. Use it where the relative magnitude matters more than the exact number.

tsx
import { Slider } from 'neba';

<Slider label="Volume" defaultValue={65} showValue />;

Props

PropTypeDefaultDescription
sizeshared'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Height and type scale
colorshared'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info''primary'Semantic colour role. Arbitrary colour values are not accepted
orientationshared'horizontal' | 'vertical''horizontal'Which way the track runs. A vertical slider needs a height of its own
valuenumber | number[]The current value. An array gives one thumb per entry
defaultValuenumber | number[]The initial value, for an uncontrolled slider
onValueChange(value, details) => voidCalled throughout the drag
onValueCommitted(value, details) => voidCalled once, when the value settles. Put the network request here
minnumber0The lowest allowed value
maxnumber100The highest allowed value
stepnumber1The granularity the value moves in
labelReactNodeThe label above the track
descriptionReactNodeHelper text below the track
showValueboolean | ((formatted, values) => ReactNode)falseShows the current value beside the label. Pass a function to format it
marksboolean | readonly SliderMark[]falsePoints named along the track, as an array of { value, label? }. true is a tick at every step
disabledbooleanfalseUnavailable
classNamesNebaSlots<'label' | 'control' | 'track' | 'indicator' | 'thumb' | 'description' | 'mark'>Class names for the parts behind the root. The root itself is className, so there is no root key

onValueChange fires throughout the drag; onValueCommitted fires once, when the value settles. Put the network request on the latter.

When an exact number has to be typed, use NumberField.

Examples

An array value makes it a range

Pass an array of numbers as the value and you get that many thumbs: a range slider. There is no separate prop for it.

min · max · step

step is the interval the thumb settles on. showValue prints the current value beside the label.

marks

marks names points along the track: 1 / 100 / 250 / 500 under a count, or the two ends of a style axis. Pass an array of { value, label? }, and a mark with no label is a tick on its own.

marks without a value is a tick at every step, which is worth pairing with a step you chose — the default step={1} over the default range would be a hundred of them, so that case draws none at all.

The row is hidden from screen readers: the thumb already announces the value and the range.

size

The thumb is drawn larger than the track: it is the part you actually hit, so it needs a real touch target.

orientation

A vertical slider has no length of its own; give it a height.

classNames

className lands on the root — the column holding the label, the strip and the line under it — and the parts inside are reached through classNames.

tsx
<Slider label="Volume" classNames={{ track: 'h-1', thumb: 'rounded-sm', mark: 'font-mono' }} />

The slots are label, control, track, indicator, thumb, description and mark. control is the whole strip a press lands on, which is taller than the track drawn inside it.

Accessibility

  • Each thumb is a real <input type="range">, so the arrow keys, Home/End and Page Up/Down all work as they should.
  • label becomes the accessible name. Without one, give the slider an aria-label.
  • showValue renders an <output>, which is announced as the value changes.
  • Hovering and dragging draw a ring around the thumb rather than changing its size.

Released under the MIT License