Slider
A headless slider component for building single-value and range inputs with pointer drag, keyboard navigation, and step snapping. Uses createSlider internally, which delegates value storage to createModel.
Usage
The Slider supports single-value and range modes. Add one Slider.Thumb for a single value, or two for a range.
Value: 50
Range: 25 – 75
<script setup lang="ts">
import { Slider } from '@vuetify/v0'
import { ref } from 'vue'
const single = ref([50])
const range = ref([25, 75])
</script>
<template>
<div class="flex flex-col gap-6">
<!-- Single -->
<div class="flex flex-col gap-2">
<Slider.Root v-model="single" class="relative flex items-center w-full h-5">
<Slider.Track class="relative h-1 w-full rounded-full bg-surface-variant">
<Slider.Range class="absolute h-full rounded-full bg-primary" />
</Slider.Track>
<Slider.Thumb class="absolute size-5 rounded-full bg-primary -translate-x-1/2 focus:outline-2 focus:outline-primary" />
</Slider.Root>
<p class="text-sm text-on-surface-variant">Value: {{ single[0] }}</p>
</div>
<!-- Range -->
<div class="flex flex-col gap-2">
<Slider.Root v-model="range" class="relative flex items-center w-full h-5" :min-steps-between-thumbs="5">
<Slider.Track class="relative h-1 w-full rounded-full bg-surface-variant">
<Slider.Range class="absolute h-full rounded-full bg-primary" />
</Slider.Track>
<Slider.Thumb class="absolute size-5 rounded-full bg-primary -translate-x-1/2 focus:outline-2 focus:outline-primary" />
<Slider.Thumb class="absolute size-5 rounded-full bg-primary -translate-x-1/2 focus:outline-2 focus:outline-primary" />
</Slider.Root>
<p class="text-sm text-on-surface-variant">Range: {{ range[0] }} – {{ range[1] }}</p>
</div>
</div>
</template>
Anatomy
<script setup lang="ts">
import { Slider } from '@vuetify/v0'
</script>
<template>
<!-- Single thumb -->
<Slider.Root>
<Slider.Track>
<Slider.Range />
</Slider.Track>
<Slider.Thumb />
</Slider.Root>
<!-- Range (two thumbs) -->
<Slider.Root>
<Slider.Track>
<Slider.Range />
</Slider.Track>
<Slider.Thumb />
<Slider.Thumb />
</Slider.Root>
<!-- With form submission -->
<Slider.Root>
<Slider.Track>
<Slider.Range />
</Slider.Track>
<Slider.Thumb />
<Slider.HiddenInput />
</Slider.Root>
</template>Architecture
The Root component composes createSlider for pointer/keyboard interaction and createModel for value storage. Each Thumb registers via a ticket and receives its position as a percentage.
The Root creates a slider instance and provides it via context. Track listens for pointer events to update the nearest thumb. Each Thumb registers itself and manages drag/keyboard interaction for its value. Range renders the filled region between thumbs (or from min to a single thumb).
Examples
Audio Equalizer
Multiple vertical sliders composed into a 5-band equalizer with preset management. Each band is an independent Slider.Root with orientation="vertical", bridged to a shared gains array via @update:model-value.
File breakdown:
| File | Role |
|---|---|
useEqualizer.ts | Composable — band definitions, gain state, named presets, apply() and reset() |
Equalizer.vue | Reusable component — renders one vertical Slider per band with dB scale and frequency labels |
equalizer.vue | Demo — wires preset buttons to the composable |
Key patterns:
Slider.Thumb v-slot="{ value, isDragging }"drives both the scale animation and the floating dB label that appears only while draggingEach band’s
Slider.Rootreceives a single-element array ([gains[index]]) and writes back via anupdatefunction that splices into the shared arrayThe composable owns all state — the component is purely presentational, making it reusable with different band configurations
HSL Color Picker
Three sliders for Hue, Saturation, and Lightness with reactive gradient tracks and a live color preview. Demonstrates how to hide Slider.Range when the track gradient is the visualization.
File breakdown:
| File | Role |
|---|---|
ColorSlider.vue | Reusable gradient slider — accepts a gradient prop for the track background and thumbColor for dynamic thumb styling |
ColorPicker.vue | Composes three ColorSliders with reactive gradients that update when hue changes, plus a color swatch and hex output |
color-picker.vue | Demo — adds clickable color presets that set all three models at once |
Key patterns:
Slider.Rangeis omitted entirely — the gradient track replaces it as the visual indicatorSlider.Thumbusesdata-[state=dragging]:scale-125for drag feedback without needing slot props — the component setsleftautomatically via its internalattrs.styleSaturation and lightness gradients are
toRefderivations that recompute when hue changes, making the tracks shift color in real timedefineModelwith named models (v-model:hue,v-model:saturation,v-model:lightness) gives the parent fine-grained control over each channel
Accessibility
Each Slider.Thumb manages its own ARIA attributes automatically.
ARIA Attributes
| Attribute | Value | Notes |
|---|---|---|
role | slider | Applied to each Thumb |
aria-valuenow | Current value | Updates on drag/keyboard |
aria-valuemin | Min value | From Root’s min prop |
aria-valuemax | Max value | From Root’s max prop |
aria-valuetext | Custom text | Optional, via Thumb prop |
aria-orientation | horizontal / vertical | Reflects Root orientation |
aria-disabled | true | When slider is disabled |
aria-readonly | true | When slider is readonly |
tabindex | 0 / removed | Removed when disabled |
Keyboard Navigation
| Key | Action |
|---|---|
ArrowRight / ArrowUp | Increment by one step |
ArrowLeft / ArrowDown | Decrement by one step |
Shift+Arrow | Increment/decrement by 10 steps |
PageUp | Increment by 10 steps |
PageDown | Decrement by 10 steps |
Home | Set to minimum |
End | Set to maximum |
Slider.Root
Props
Events
update:model-value
[value: number | number[]]start
[value: number | number[]]end
[value: number | number[]]Slots
default
SliderRootSlotPropsSlider.HiddenInput
Props
Slider.Range
Props
Slots
default
SliderRangeSlotPropsSlider.Thumb
Props
Slots
default
SliderThumbSlotPropsSlider.Track
Props
Slots
default
SliderTrackSlotPropsRecipes
Form Integration
Set name on Root to auto-render hidden inputs for form submission — one per thumb:
<template>
<Slider.Root name="price" :min="0" :max="1000">
<Slider.Track>
<Slider.Range />
</Slider.Track>
<Slider.Thumb />
</Slider.Root>
</template>Drag Events
Root emits start and end for pointer drag lifecycle:
<template>
<Slider.Root
v-model="value"
@start="onStart"
@end="onEnd"
>
<Slider.Track>
<Slider.Range />
</Slider.Track>
<Slider.Thumb />
</Slider.Root>
</template>Data Attributes
Style interactive states without slot props:
<template>
<Slider.Thumb class="data-[state=dragging]:scale-125 transition-transform" />
</template>| Attribute | Values | Components |
|---|---|---|
data-state | dragging, idle | Thumb |
data-disabled | true | Root, Track, Range, Thumb |
data-readonly | true | Root, Track, Range, Thumb |
data-orientation | horizontal, vertical | Root, Track, Range |