# Slider

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

Source: [https://askrjs.com/docs/components/slider](https://askrjs.com/docs/components/slider)

Status: stable. Packages: @askrjs/ui/slider, @askrjs/themes/slider.

**Published packages are authoritative.** Examples may lag behind a published contract. When guidance differs, verify the exports and TypeScript declarations in your installed package, then file an issue.

## Example

```tsx
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>
```

## Published props

Generated from the TypeScript declarations shipped by the installed package.

### `SliderOwnProps`

Import from `@askrjs/ui/slider`.

- `children?: unknown;` — Supports literal, nested, array-mapped, and `For`-rendered descendants.
- `defaultValue?: number | undefined;`
- `disabled?: boolean | undefined;`
- `id?: string | undefined;`
- `max?: number | undefined;`
- `min?: number | undefined;`
- `name?: string | undefined;`
- `onValueChange?: ((value: number) => void) | undefined;`
- `orientation?: SliderOrientation | undefined;`
- `step?: number | undefined;`
- `value?: number | undefined;`

### `SliderProps`

Import from `@askrjs/ui/slider`.

- `asChild?: false | undefined;`
- `children?: unknown;` — Supports literal, nested, array-mapped, and `For`-rendered descendants.
- `defaultValue?: number | undefined;`
- `disabled?: boolean | undefined;`
- `id?: string | undefined;`
- `max?: number | undefined;`
- `min?: number | undefined;`
- `name?: string | undefined;`
- `onValueChange?: ((value: number) => void) | undefined;`
- `orientation?: SliderOrientation | undefined;`
- `ref?: Ref<HTMLDivElement>;`
- `step?: number | undefined;`
- `value?: number | undefined;`

### `SliderRangeAsChildProps`

Import from `@askrjs/ui/slider`.

- `asChild: true;`
- `children: JSXElement;`
- `ref?: Ref<Element>;`

### `SliderRangeProps`

Import from `@askrjs/ui/slider`.

- `asChild?: false | undefined;`
- `children?: unknown;`
- `ref?: Ref<HTMLDivElement>;`

### `SliderThumbAsChildProps`

Import from `@askrjs/ui/slider`.

- `asChild: true;`
- `children: JSXElement;`
- `ref?: Ref<Element>;`

### `SliderThumbProps`

Import from `@askrjs/ui/slider`.

- `asChild?: false | undefined;`
- `children?: unknown;`
- `ref?: Ref<HTMLDivElement>;`

### `SliderTrackAsChildProps`

Import from `@askrjs/ui/slider`.

- `asChild: true;`
- `children: JSXElement;`
- `ref?: Ref<Element>;`

### `SliderTrackProps`

Import from `@askrjs/ui/slider`.

- `asChild?: false | undefined;`
- `children?: unknown;`
- `ref?: 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.

## Related pages

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.

## Documentation navigation

[Previous](https://askrjs.com/docs/components/select/index.md) | [Next](https://askrjs.com/docs/components/switch/index.md)
