Inputs
Slider
Pick a value, or a range with two thumbs, by dragging or with the keyboard. The current value is shown next to the label and formatted the way you need: currency, percentages, days.
Invoice amount
Showing 38 invoices between $1,200 and $4,800.
"use client"; import { useState } from "react";import { Slider } from "@/components/ui/slider"; const usd = (n: number) => `$${n.toLocaleString("en-US")}`; export function PriceFilterDemo() { const [range, setRange] = useState<[number, number]>([1200, 4800]); return ( <div className="mx-auto w-full max-w-sm rounded-ui-lg border border-line bg-surface p-5 shadow-ui"> <Slider label="Invoice amount" value={range} onValueChange={setRange} min={0} max={10000} step={100} formatValue={usd} marks={[{ value: 0 }, { value: 5000 }, { value: 10000 }]} /> <p className="mt-4 text-[13px] leading-5 text-muted"> Showing <span className="font-medium text-fg tabular">38</span> invoices between {usd(range[0])} and {usd(range[1])}. </p> </div> );}Usage
components/ui/slider.tsx
import { Slider } from "@/components/ui/slider"; export function Example() { return <Slider label="Discount" defaultValue={15} max={50} formatValue={(n) => `${n}%`} />;}Examples
Range
Pass a [from, to] pair to get two thumbs. They can't cross each other.
Invoice amount
Showing 38 invoices between $1,200 and $4,800.
"use client"; import { useState } from "react";import { Slider } from "@/components/ui/slider"; const usd = (n: number) => `$${n.toLocaleString("en-US")}`; export function PriceFilterDemo() { const [range, setRange] = useState<[number, number]>([1200, 4800]); return ( <div className="mx-auto w-full max-w-sm rounded-ui-lg border border-line bg-surface p-5 shadow-ui"> <Slider label="Invoice amount" value={range} onValueChange={setRange} min={0} max={10000} step={100} formatValue={usd} marks={[{ value: 0 }, { value: 5000 }, { value: 10000 }]} /> <p className="mt-4 text-[13px] leading-5 text-muted"> Showing <span className="font-medium text-fg tabular">38</span> invoices between {usd(range[0])} and {usd(range[1])}. </p> </div> );}Single value
Team seats
<div className="mx-auto flex w-full max-w-sm flex-col gap-8"> <Slider label="Team seats" defaultValue={12} min={1} max={50} /> <Slider aria-label="Volume" defaultValue={40} showValue={false} /></div>Steps and formatting
step can be a decimal. formatValue changes the visible value and what screen readers hear (aria-valuetext).
Late fee
Reminder after
"use client"; import { Slider } from "@/components/ui/slider"; export function FormattedSliderDemo() { return ( <div className="mx-auto flex w-full max-w-sm flex-col gap-8"> <Slider label="Late fee" defaultValue={1.5} min={0} max={5} step={0.25} formatValue={(n) => `${n.toFixed(2)}%`} /> <Slider label="Reminder after" defaultValue={7} min={1} max={30} formatValue={(n) => `${n} ${n === 1 ? "day" : "days"}`} /> </div> );}Disabled
Storage limit (GB)
<div className="mx-auto w-full max-w-sm"> <Slider label="Storage limit (GB)" defaultValue={[20, 80]} disabled /></div>Props
PropTypeDefaultDescription
labelReactNodedefault: —Visible label above the track. Without it, pass aria-label.value / defaultValuenumber | [number, number]default: —One number for a single thumb, a pair for a range.onValueChange(value) => voiddefault: —Fires on every change while dragging or pressing keys.onValueCommit(value) => voiddefault: —Fires when a drag or key press ends; good for saving.min / maxnumberdefault: 0 / 100Bounds.stepnumberdefault: 1Increment; decimals are fine.showValuebooleandefault: trueShow the formatted value in the label row.formatValue(n: number) => stringdefault: —Formats the shown value and aria-valuetext.marks{ value: number; label?: ReactNode }[]default: —Tick labels under the track.disabledbooleandefault: —Dims the slider and ignores input.namestringdefault: —Adds hidden inputs for forms (name, or name[0] and name[1]).
Accessibility
- Each thumb is a role="slider" with aria-valuemin, aria-valuemax, aria-valuenow and a formatted aria-valuetext.
- Arrow keys move by one step, Page Up and Page Down by a tenth of the range, Home and End jump to the bounds.
- Range thumbs are named “Minimum” and “Maximum” plus the label, and each one's bounds stop at the other thumb.
- Clicking anywhere on the track moves the nearest thumb and focuses it, so you can continue with the keyboard.
- The thumb is 18px with a larger hit area from the 20px track row; touch dragging doesn't scroll the page.