# Shadcn Slider

> Free shadcn/ui slider component for React — built on Radix. A draggable range input: a single thumb, a two-thumb range, a value label, stepped ticks, and a volume control.

Source: https://designrevision.com/components/slider

The **Shadcn Slider** is a draggable **range input** for React, built on <a href="https://www.radix-ui.com/primitives/docs/components/slider" rel="nofollow">Radix</a>. Drag a thumb along the track to pick a value — or add a second thumb for a min/max range. Radix handles the dragging, keyboard, and ARIA; the shadcn styling gives you the track, range, and thumb. The examples below cover a basic slider, a price range, a value label, a stepped scale, and a volume control.

## The shape

```tsx
<Slider defaultValue={[50]} min={0} max={100} step={1} />
```

The value is always an **array** — one entry per thumb. A single-element array is a normal slider; a two-element array is a range.

## Range slider

Pass two values and Radix renders two thumbs:

```tsx
<Slider defaultValue={[200, 800]} min={0} max={1000} step={10} />
```

Read both ends back from `onValueChange`, which fires with the full array on every drag.

## Reading the value

Control it to show or sync the value:

```tsx
const [value, setValue] = React.useState([50])

<Slider value={value} onValueChange={setValue} />
<span>{value[0]}</span>
```

For a form that only needs the value on submit, use `defaultValue` and read it from the field instead.

## Steps and scale

`step` snaps the thumb; `min` and `max` bound it:

```tsx
<Slider min={0} max={100} step={20} />
```

For a labelled scale, render your own tick marks below the track and space them to line up with the steps — the **Stepped with ticks** example shows the pattern.

## Common patterns

- **Price range** — a two-thumb range with a `$min – $max` readout (the Range example).
- **Brightness / opacity** — a single value with a live percentage (the Value label example).
- **Volume** — a slider between mute and volume icons (the Volume control example).

## Accessibility

Each thumb is focusable and driven by the keyboard: Arrow keys step, Page Up / Page Down jump, and Home / End snap to min / max. Radix sets `role="slider"` with `aria-valuemin`, `aria-valuemax`, and `aria-valuenow`, so the value is announced. Add an `aria-label` (or a visible `<Label>`) so the control's purpose is clear.

## Installation

### slider

**Install:**

```bash
npx shadcn@latest add @designrevision/slider
```

**Dependencies:** @radix-ui/react-slider, utils

**Props**

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| defaultValue / value | number[] | [min, max] | Thumb positions. One number = one thumb; two = a range slider. |
| min / max / step | number | 0 / 100 / 1 | Range bounds and increment. |
| orientation | "horizontal" \| "vertical" | "horizontal" | Slider direction. |

**Usage & accessibility:** A Radix slider. Pass an array to value / defaultValue — one number renders a single thumb, two render a range slider. Set min, max, step for the scale and onValueChange to read it. Works horizontally or vertically (orientation="vertical" needs a height).

```tsx
// components/ui/slider.tsx
"use client"

import * as React from "react"
import * as SliderPrimitive from "@radix-ui/react-slider"

import { cn } from "@/lib/utils"

function Slider({
  className,
  defaultValue,
  value,
  min = 0,
  max = 100,
  ...props
}: React.ComponentProps<typeof SliderPrimitive.Root>) {
  const _values = React.useMemo(
    () =>
      Array.isArray(value)
        ? value
        : Array.isArray(defaultValue)
          ? defaultValue
          : [min, max],
    [value, defaultValue, min, max]
  )

  return (
    <SliderPrimitive.Root
      data-slot="slider"
      defaultValue={defaultValue}
      value={value}
      min={min}
      max={max}
      className={cn(
        "relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col",
        className
      )}
      {...props}
    >
      <SliderPrimitive.Track
        data-slot="slider-track"
        className={cn(
          "bg-muted relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5"
        )}
      >
        <SliderPrimitive.Range
          data-slot="slider-range"
          className={cn(
            "bg-primary absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full"
          )}
        />
      </SliderPrimitive.Track>
      {Array.from({ length: _values.length }, (_, index) => (
        <SliderPrimitive.Thumb
          data-slot="slider-thumb"
          key={index}
          className="border-primary bg-background ring-ring/50 block size-4 shrink-0 rounded-full border shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
        />
      ))}
    </SliderPrimitive.Root>
  )
}

export { Slider }
```

## Examples

### Basic

The canonical single-thumb slider — drag to pick one value.

**Install:**

```bash
npx shadcn@latest add @designrevision/slider-demo-01
```

**Dependencies:** slider

```tsx
// components/ui/slider-demo-01.tsx
import { Slider } from "@/components/ui/slider"

export default function SliderDemo01() {
  return (
    <Slider
      defaultValue={[50]}
      max={100}
      step={1}
      className="w-full max-w-sm"
    />
  )
}
```

---

### Range

Two thumbs select a min and max, with a live price readout.

**Install:**

```bash
npx shadcn@latest add @designrevision/slider-range-01
```

**Dependencies:** slider

```tsx
// components/ui/slider-range-01.tsx
"use client"

import * as React from "react"

import { Slider } from "@/components/ui/slider"

export default function SliderRange01() {
  const [value, setValue] = React.useState([200, 800])

  return (
    <div className="w-full max-w-sm space-y-3">
      <div className="flex items-center justify-between text-sm">
        <span className="text-muted-foreground">Price range</span>
        <span className="font-medium tabular-nums">
          ${value[0]} – ${value[1]}
        </span>
      </div>
      <Slider
        value={value}
        onValueChange={setValue}
        min={0}
        max={1000}
        step={10}
      />
    </div>
  )
}
```

---

### With value label

A controlled slider that shows its current value beside a label.

**Install:**

```bash
npx shadcn@latest add @designrevision/slider-value-01
```

**Dependencies:** slider, label

```tsx
// components/ui/slider-value-01.tsx
"use client"

import * as React from "react"

import { Label } from "@/components/ui/label"
import { Slider } from "@/components/ui/slider"

export default function SliderValue01() {
  const [value, setValue] = React.useState([33])

  return (
    <div className="w-full max-w-sm space-y-3">
      <div className="flex items-center justify-between">
        <Label>Brightness</Label>
        <span className="text-sm font-medium tabular-nums">{value[0]}%</span>
      </div>
      <Slider value={value} onValueChange={setValue} max={100} step={1} />
    </div>
  )
}
```

---

### Stepped with ticks

Snaps to fixed steps, with tick labels marked below the track.

**Install:**

```bash
npx shadcn@latest add @designrevision/slider-steps-01
```

**Dependencies:** slider

```tsx
// components/ui/slider-steps-01.tsx
import { Slider } from "@/components/ui/slider"

const ticks = [0, 20, 40, 60, 80, 100]

export default function SliderSteps01() {
  return (
    <div className="w-full max-w-sm space-y-3">
      <Slider defaultValue={[40]} max={100} step={20} />
      <div className="text-muted-foreground flex justify-between text-xs tabular-nums">
        {ticks.map((tick) => (
          <span key={tick}>{tick}</span>
        ))}
      </div>
    </div>
  )
}
```

---

### Volume control

A slider flanked by mute and volume icons — the media-control pattern.

**Install:**

```bash
npx shadcn@latest add @designrevision/slider-volume-01
```

**Dependencies:** lucide-react, slider

```tsx
// components/ui/slider-volume-01.tsx
"use client"

import * as React from "react"
import { Volume2, VolumeX } from "lucide-react"

import { Slider } from "@/components/ui/slider"

export default function SliderVolume01() {
  const [volume, setVolume] = React.useState([60])

  return (
    <div className="flex w-full max-w-sm items-center gap-3">
      <VolumeX className="text-muted-foreground size-4 shrink-0" />
      <Slider
        value={volume}
        onValueChange={setVolume}
        max={100}
        step={1}
        aria-label="Volume"
      />
      <Volume2 className="text-muted-foreground size-4 shrink-0" />
    </div>
  )
}
```
