Components
- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Attachment
- Avatar
- Badge
- Breadcrumb
- Bubble
- Button
- Button Group
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Collapsible
- Combobox
- Command
- Context Menu
- Date Picker
- Dialog
- Drawer
- Dropdown Menu
- Empty
- Field
- Hover Card
- Input
- Input OTP
- Input Group
- Item
- Kbd
- Label
- Marker
- Menubar
- Message
- Message Scroller
- Native Select
- Navigation Menu
- Pagination
- Popover
- Progress
- Radio Group
- Resizable
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Spinner
- Switch
- Table
- Tabs
- Textarea
- Toggle
- Toggle Group
- Tooltip
- New Component
Elements
Charts
Icons
Volume40%
Price range$20 – $70
Step (25)50
Disabled
import { useState } from "react";
import { Slider } from "@/components/ui/slider";
export const SliderDemo = () => {
const [single, setSingle] = useState([40]);
const [range, setRange] = useState([20, 70]);
const [stepped, setStepped] = useState([50]);
return (
<div className="flex flex-col gap-8 px-2">
{/* Default */}
<div className="flex flex-col gap-2">
<div className="text-muted-foreground flex justify-between text-xs">
<span>Volume</span>
<span>{single[0]}%</span>
</div>
<Slider value={single} onValueChange={setSingle} min={0} max={100} sounds />
</div>
{/* Range */}
<div className="flex flex-col gap-2">
<div className="text-muted-foreground flex justify-between text-xs">
<span>Price range</span>
<span>${range[0]} – ${range[1]}</span>
</div>
<Slider value={range} onValueChange={setRange} min={0} max={100} sounds />
</div>
{/* Stepped */}
<div className="flex flex-col gap-2">
<div className="text-muted-foreground flex justify-between text-xs">
<span>Step (25)</span>
<span>{stepped[0]}</span>
</div>
<Slider value={stepped} onValueChange={setStepped} min={0} max={100} step={25} sounds />
</div>
{/* Disabled */}
<div className="flex flex-col gap-2">
<span className="text-muted-foreground text-xs">Disabled</span>
<Slider defaultValue={[60]} disabled />
</div>
{/* Vertical */}
<div className="flex items-end gap-6">
{[30, 60, 80].map((v, i) => (
<Slider key={i} defaultValue={[v]} orientation="vertical" className="h-28" />
))}
</div>
</div>
);
};Range slider
Pass two values to defaultValue (or value) to render a range slider with two thumbs.
<Slider defaultValue={[20, 70]} min={0} max={100} />Step
<Slider defaultValue={[50]} step={25} />Vertical
<Slider defaultValue={[60]} orientation="vertical" className="h-32" />Controlled
const [value, setValue] = useState([40]);
<Slider value={value} onValueChange={setValue} />With sounds
Pass sounds to play a tick on every value change.
<Slider defaultValue={[50]} sounds />Props
| Prop | Type | Default | Description |
|---|---|---|---|
defaultValue | number[] | [0] | Uncontrolled initial value |
value | number[] | — | Controlled value |
onValueChange | (value: number[]) => void | — | Fires on every thumb move |
onValueCommit | (value: number[]) => void | — | Fires on pointer up / key release |
min | number | 0 | Minimum value |
max | number | 100 | Maximum value |
step | number | 1 | Step interval |
orientation | "horizontal" | "vertical" | "horizontal" | Slider direction |
disabled | boolean | false | Disables interaction |
sounds | boolean | false | Plays tick audio on value change |
Usage
import { Slider } from "@/components/ui/slider";
<Slider defaultValue={[50]} max={100} step={1} />Specs
# Mint — Slider Specs
## Component
- name: slider
- file: registry/mint/slider.tsx
- installs to: components/ui/slider.tsx
- dependencies: radix-ui
- registryDependencies: none
## What is it
A Radix UI Slider wrapper for selecting a numeric value or a range. Thumbs
are auto-generated from the length of the value/defaultValue array — pass one
value for a single thumb, two for a range slider with two thumbs. Prefer
Slider over a plain input[type=range] when you need range selection, step
snapping, vertical orientation, or audio feedback.
## Visual identity
Minimal and tactile. A muted track with a primary-coloured fill and a
circular thumb with a subtle shadow creates clear affordance without visual
noise. The rounded-full geometry is consistent with Mint's radius
language. In dark mode the thumb's background inverts to the dark surface
colour, keeping contrast clean.
## Design tokens
--muted: oklch(0.96 0.02 155) / oklch(0.2 0.03 155) — track background
--primary: oklch(0.5 0.14 155) / oklch(0.62 0.15 155) — range fill and thumb border
--background: oklch(1 0 0) / oklch(0.145 0 0) — thumb fill
--ring: oklch(0.5 0.14 155) / oklch(0.62 0.15 155) — focus ring on thumb (ring-ring/50)
## Sub-components
- Slider (Root) → outer positioning container; owns orientation, disabled, and data-slot="slider"
- SliderTrack → full-width/height muted bar; data-slot="slider-track"
- SliderRange → primary-coloured fill between origin and thumb(s); data-slot="slider-range"
- SliderThumb → draggable circular handle, one per value entry; data-slot="slider-thumb"
## States
- focus-visible: 3px ring in --ring/50 on the thumb; outline-none suppresses default
- disabled: data-[disabled]:opacity-50 on root; disabled:pointer-events-none on thumb
- orientation=vertical: data-[orientation=vertical] swaps flex direction, width, and height classes on root, track, and range
## Conventions
- Range slider: pass two values to defaultValue/value (e.g. defaultValue={[20, 70]})
- Vertical sliders require an explicit height on the Slider element (e.g. className="h-32")
- sounds / haptic: GIN Design exclusive — plays a "tick" audio via useFeedback on every onValueChange
- Sub-components expose data-slot="slider", data-slot="slider-track", data-slot="slider-range", and data-slot="slider-thumb" for CSS targeting
- Thumbs are rendered dynamically from the value array length — do not render SliderThumb manually
- Always provide an accessible label via aria-label or an associated <label> elementInstallation
$ npx shadcn add \https://gin-design.vercel.app/slider