Askr documentation
UI & Components

Slider

Slider: anatomy, keyboard behavior, state, and theming in Askr.

Example

import { Field, FieldLabel, Slider } from '@askrjs/themes/components';

<Field>
  <FieldLabel htmlFor="capacity">Capacity: {capacity()}</FieldLabel>
  <Slider id="capacity" min={1} max={100} value={capacity()} onValueChange={setCapacity} />
</Field>
Try Slider

Adjust a value with pointer or keyboard input.

Published props

Generated from the TypeScript declarations shipped by the installed package. Named types in the Type column define the accepted values.

SliderOwnProps

Import from @askrjs/ui/slider.

PropTypeDefaultDescription
childrenchildren?: unknown;Supports literal, nested, array-mapped, and `For`-rendered descendants.
defaultValuedefaultValue?: number | undefined;
disableddisabled?: boolean | undefined;
idid?: string | undefined;
maxmax?: number | undefined;
minmin?: number | undefined;
namename?: string | undefined;
onValueChangeonValueChange?: ((value: number) => void) | undefined;
orientationorientation?: SliderOrientation | undefined;
stepstep?: number | undefined;
valuevalue?: number | undefined;

SliderProps

Import from @askrjs/ui/slider.

PropTypeDefaultDescription
asChildasChild?: false | undefined;
childrenchildren?: unknown;Supports literal, nested, array-mapped, and `For`-rendered descendants.
defaultValuedefaultValue?: number | undefined;
disableddisabled?: boolean | undefined;
idid?: string | undefined;
maxmax?: number | undefined;
minmin?: number | undefined;
namename?: string | undefined;
onValueChangeonValueChange?: ((value: number) => void) | undefined;
orientationorientation?: SliderOrientation | undefined;
refref?: Ref<HTMLDivElement>;
stepstep?: number | undefined;
valuevalue?: number | undefined;

SliderRangeAsChildProps

Import from @askrjs/ui/slider.

PropTypeDefaultDescription
asChildasChild: true;
childrenchildren: JSXElement;
refref?: Ref<Element>;

SliderRangeProps

Import from @askrjs/ui/slider.

PropTypeDefaultDescription
asChildasChild?: false | undefined;
childrenchildren?: unknown;
refref?: Ref<HTMLDivElement>;

SliderThumbAsChildProps

Import from @askrjs/ui/slider.

PropTypeDefaultDescription
asChildasChild: true;
childrenchildren: JSXElement;
refref?: Ref<Element>;

SliderThumbProps

Import from @askrjs/ui/slider.

PropTypeDefaultDescription
asChildasChild?: false | undefined;
childrenchildren?: unknown;
refref?: Ref<HTMLDivElement>;

SliderTrackAsChildProps

Import from @askrjs/ui/slider.

PropTypeDefaultDescription
asChildasChild: true;
childrenchildren: JSXElement;
refref?: Ref<Element>;

SliderTrackProps

Import from @askrjs/ui/slider.

PropTypeDefaultDescription
asChildasChild?: false | undefined;
childrenchildren?: unknown;
refref?: Ref<HTMLDivElement>;

Purpose

Drag a thumb along a track to pick a number within `min`/`max` — that's what `Slider` renders and manages, from volume controls to price filters. Pointer dragging, keyboard stepping, and value snapping are all handled internally, so you never have to write pointer-move math by hand. Multiple parts (`SliderTrack`, `SliderRange`, `SliderThumb`) share state through a compound id rather than props drilling.

Install and import

Install `@askrjs/ui` for the behavior and `@askrjs/themes` for the default track/range/thumb visuals, then import from `@askrjs/ui/slider` and `@askrjs/themes/slider`. Both subpaths are real exports, so there's no need to pull in the package roots just to use a slider.

Live examples

The examples show single-thumb sliders at different `min`, `max`, and `step` configurations, including a disabled variant. Each one is built from the same four parts — `Slider`, `SliderTrack`, `SliderRange`, `SliderThumb` — so you can trace exactly which prop changed the behavior you're looking at.

Anatomy

`Slider` is the state-owning root; inside it you compose `SliderTrack` (the full-length background), `SliderRange` (the filled portion between the minimum and the current value), and `SliderThumb` (the draggable, focusable handle). All three children are plain `div` wrappers with no required props beyond what `BoxProps` already gives you.

State model

The numeric `value` can be left uncontrolled with `defaultValue` or driven externally through `value` and `onValueChange`, alongside `min`, `max`, `step`, `orientation` (`horizontal` or `vertical`), and `disabled`. Dragging the thumb or pressing arrow keys calls the same internal setter that feeds `onValueChange`, so there's no separate internal-vs-external value to reconcile. A per-slider registry — a `WeakMap` keyed by an internal identity object, not the string `sliderId` — tracks the track and thumb DOM nodes needed for pointer math; `sliderId` itself is only used to derive the track/thumb DOM element ids, not as the registry key.

Keyboard and accessibility

The thumb carries `role="slider"` with `aria-valuenow`, `aria-valuemin`, `aria-valuemax`, and `aria-orientation` kept in sync automatically, matching the `SLIDER_A11Y_CONTRACT` export. Dragging is handled with `pointermove`/`pointerup` listeners attached only while a drag is active, and every drag ends by returning focus to the thumb so keyboard users can immediately take over with arrow keys.

Styling and tokens

Track, range, and thumb each expose `data-slot` for targeted styling, and the root forwards `data-disabled` and `data-orientation` so vertical sliders and disabled states can be styled without JavaScript branching. `@askrjs/themes/slider` ships sensible defaults for all of this; override the CSS custom properties rather than fighting the generated class names.

API

Beyond the value props, `Slider` takes `id` and `name` — set `name` if you want the current value to participate in a surrounding `<form>`. Values are snapped to the nearest valid `step` before `onValueChange` fires, and if `max` is configured at or below `min` the library quietly falls back to `min + step` rather than producing a zero-width or inverted track.

Edge cases

Dragging outside the track's bounding box still resolves to a valid value — the raw pointer position is snapped to the nearest `step` first, then the snapped result is clamped through `min`/`max` (not the other way around), so users don't lose the drag by moving the cursor too far. Because listeners are attached per active drag and torn down on `pointerup`, unmounting a `Slider` mid-drag doesn't leave stray global listeners behind.

For a fixed, non-interactive value indicator rather than an input, use Progress instead — Slider is specifically for values the user changes. If you need a numeric input that also supports typing an exact value, pair it with Input rather than trying to make the thumb double as a text field.