Slider

An input where the user selects a value from within a given range.

Basic
Range
Multiple Thumbs
Vertical
Controlled
0.3, 0.7
Disabled

Installation

Install the dependencies

pnpm add cn github:sveltery/base

Copy the component

Copy the following files into src/lib/components/ui/slider.

src/lib/components/ui/slider/Slider.svelte
<script lang="ts">
  import { Slider as Primitive } from '@sveltery/base/slider';
  import { cn } from 'cn';
  import type { SliderProps } from './types.js';
  let { value = $bindable(), defaultValue, min = 0, max = 100, class: classProp, ...props }: SliderProps = $props();
  const current = $derived(value ?? defaultValue);
  const thumbs = $derived(Array.isArray(current) ? current.length : 1);
</script>
<Primitive.Root data-slot="slider" {defaultValue} bind:value {min} {max} thumbAlignment="edge" {...props} class={cn('data-horizontal:w-full data-vertical:h-full', classProp)}>
  <Primitive.Control class="data-vertical:min-h-40 relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-vertical:h-full data-vertical:w-auto data-vertical:flex-col">
    <Primitive.Track data-slot="slider-track" class="bg-muted rounded-full data-horizontal:h-1 data-horizontal:w-full data-vertical:h-full data-vertical:w-1 relative grow overflow-hidden select-none">
      <Primitive.Indicator data-slot="slider-range" class="bg-primary select-none data-horizontal:h-full data-vertical:w-full" />
    </Primitive.Track>
    {#each { length: thumbs } as _, index (index)}
      <Primitive.Thumb data-slot="slider-thumb" class="border-ring ring-ring/50 relative size-3 rounded-full border bg-white transition-[color,box-shadow] after:absolute after:-inset-2 hover:ring-3 focus-visible:ring-3 focus-visible:outline-hidden active:ring-3 block shrink-0 select-none disabled:pointer-events-none disabled:opacity-50" />
    {/each}
  </Primitive.Control>
</Primitive.Root>
src/lib/components/ui/slider/types.ts
import type { SliderRootProps } from '@sveltery/base/slider';
export type SliderProps = Omit<SliderRootProps, 'children'>;
export type { SliderRootState as SliderState, SliderValue, SliderChangeEventDetails, SliderCommitEventDetails } from '@sveltery/base/slider';
src/lib/components/ui/slider/index.ts
export { default as Slider } from './Slider.svelte';
export type { SliderProps, SliderState, SliderValue, SliderChangeEventDetails, SliderCommitEventDetails } from './types.js';

Update the import paths

Change the imports to match your project setup.

Usage

<script lang="ts">
  import { Slider } from '$lib/components/ui/slider';
</script>
<Slider defaultValue={50} max={100} step={1} />

Value

Bind value to read and write the position. defaultValue is the position an unbound slider starts from. A number renders one thumb, and an array renders one thumb per item.

<script lang="ts">
  let range = $state([25, 75]);
</script>

<Slider bind:value={range} min={0} max={100} step={5} />

Vertical

Give a vertical slider a height.

<Slider defaultValue={[50]} orientation="vertical" class="h-40" />

Forms

Each thumb holds a range input, so the slider submits one entry per thumb under name.

<form method="POST">
  <Slider name="price" defaultValue={[20, 80]} />
  <button type="submit">Filter</button>
</form>

API reference

Slider is styled on top of the Sveltery Base Slider primitive, which owns its value, pointer and keyboard behavior and accessibility. See the Base UI Slider documentation for the behavior it ports.