createSlider
A composable for managing slider state: value math, step snapping, percentage conversion, and multi-thumb value operations. Designed for single-thumb, range, and multi-thumb sliders.
Usage
The createSlider composable manages a number[] of thumb values with configurable min/max/step bounds. It provides pure math functions (snap, fromValue, fromPercent) and index-based thumb operations (set, up, down).
import { createSlider } from '@vuetify/v0'
const slider = createSlider({ min: 0, max: 100, step: 5 })
// Each thumb is a registered ticket with a shallowRef<number> value
const thumb = slider.register({ value: 50 })
slider.up(0) // values: [55]
slider.fromValue(50) // 50
slider.snap(47) // 45 (nearest step of 5)
// Range — register two thumbs
const slider2 = createSlider({ min: 0, max: 100, step: 1 })
slider2.register({ value: 25 })
slider2.register({ value: 75 })
slider2.set(0, 30) // values: [30, 75]
slider2.set(1, 60) // values: [30, 60]Reactivity
Slider state is always reactive. Values and derived properties update automatically.
| Property/Method | Reactive | Notes |
|---|---|---|
values | Ref — all thumb values | |
disabled | Ref — accepts MaybeRefOrGetter | |
orientation | Ref — accepts MaybeRefOrGetter | |
inverted | Ref — accepts MaybeRefOrGetter | |
snap | Pure function — rounds to nearest step | |
fromValue | Pure function — value to percentage | |
fromPercent | Pure function — percentage to value |
Value constraints set automatically clamps to min/max, snaps to the nearest step, and enforces minimum distance between adjacent thumbs via minStepsBetweenThumbs.
Examples
Media Scrubber
A music player scrubber built entirely with createSlider — no Slider.* components needed. Demonstrates how the composable’s math functions power custom pointer interactions.
Every pointer interaction follows the same loop: pointer → fromPercent() → set() → fromValue() → UI. The provider converts raw clientX into a track percentage, fromPercent snaps it to the nearest 0.1-second step, and the consumer reads values[0] back through fromValue() to position the playhead and color the waveform bars.
| File | Role |
|---|---|
context.ts | Typed context with createContext, shared constants |
ScrubberProvider.vue | Creates slider, owns pointer logic, provides via slot |
ScrubberConsumer.vue | Injects context, renders waveform and playhead |
scrubber.vue | Entry point wiring provider to consumer |
Key patterns:
Provider owns all pointer math — the consumer never touches
PointerEventfromPercent+sethandle step snapping and clamping automaticallyscrubbingref drives CSS transitions — instant updates while dragging, smooth otherwise
Click and drag across the waveform to scrub through the track.
Theme Comparison
A before/after theme comparison tool — the same fromPercent → set pointer math as the scrubber, applied to a completely different visual metaphor. The slider never renders as a traditional slider control.
Same math, different metaphor This uses the exact same fromPercent → set → fromValue loop as the scrubber above, proving createSlider is a reusable math primitive — not a UI widget.
Two identical UI panels are stacked with position: absolute. The bottom layer has data-theme="light", the top has data-theme="dark" with clip-path: inset(0 0 0 X%) where X comes from slider.fromValue(). Dragging the handle clips the dark panel from the left, revealing the light panel underneath.
| File | Role |
|---|---|
useCompare.ts | Creates slider, exposes reactive split and pointer handlers |
CompareDisplay.vue | Renders light/dark panels with clip-path driven by split |
compare.vue | Entry point wiring the composable to the display |
Key patterns:
createSlideras a math primitive — no form input, noSlider.*componentsdata-themescoping — two theme contexts coexist in the same DOM treeSame pointer math pattern as the scrubber, proving the composable is reusable across visual metaphors
Drag the divider handle left and right to compare themes.
Functions
createSlider
(options?: SliderOptions) => SliderContextCreates slider state with value math, step snapping, and multi-thumb support. Extends createModel for useProxyModel compatibility. Each thumb is a model ticket with a shallowRef<number> value. Values are derived from the ordered tickets, enabling proper registration/unregistration lifecycle.
Options
step
number | undefinedStep increment. Values are snapped to the nearest multiple of `step` above `min`.
Default: 1
readonly
MaybeRefOrGetter<boolean> | undefinedWhether the slider is readonly (focusable but not editable).
Properties
disabled
Readonly<Ref<boolean, boolean>>Whether disabled. Reactive ref derived from the `disabled` option.
readonly
Readonly<Ref<boolean, boolean>>Whether readonly. Reactive ref derived from the `readonly` option.
orientation
Readonly<Ref<"horizontal" | "vertical", "horizontal" | "vertical">>Slider orientation. Reactive ref derived from the `orientation` option.
inverted
Readonly<Ref<boolean, boolean>>Whether inverted. Reactive ref derived from the `inverted` option.
Methods
apply
(values: unknown[], options?: { multiple?) => voidApply external values — snaps, constrains, and writes to ticket refs.
register
(input?: number | { value) => ModelTicket<SliderTicketInput>Register a new thumb and return its ticket.
set
(index: number, value: number) => voidSet the value at a thumb index with step snapping and neighbor constraints.