Componentform
Slider
Draggable value slider with chunky thumb.
Preview
Live Preview
60%
Installation
Tokens guide →Add to your project via CLI (zero dependencies, code you own):
npx bigbullui add sliderOr install the full npm package:
npm install bigbulluiUsage
import { Slider } from "@/components/ui/slider";
<Slider defaultValue={60} aria-label="Volume" />Source code
slider.tsxZero dependencies (only React + utils). Copy and paste directly into your project:
"use client";
import * as React from "react";
import { cn } from "./lib/utils";
export type SliderProps = {
value?: number;
defaultValue?: number;
min?: number;
max?: number;
step?: number;
onValueChange?: (value: number) => void;
disabled?: boolean;
"aria-label"?: string;
className?: string;
};
const Slider = React.forwardRef<HTMLInputElement, SliderProps>(
(
{
value,
defaultValue = 50,
min = 0,
max = 100,
step = 1,
onValueChange,
disabled,
className,
...props
},
ref
) => {
const [internal, setInternal] = React.useState(defaultValue);
const current = value ?? internal;
const percent = max > min ? ((current - min) / (max - min)) * 100 : 0;
return (
<input
ref={ref}
type="range"
min={min}
max={max}
step={step}
value={current}
disabled={disabled}
onChange={(event) => {
const next = Number(event.target.value);
if (value === undefined) setInternal(next);
onValueChange?.(next);
}}
style={{ "--fill-pct": `${percent}%` } as React.CSSProperties}
className={cn(
"h-6 w-full cursor-pointer appearance-none bg-transparent disabled:pointer-events-none disabled:opacity-50 [&::-webkit-slider-runnable-track]:h-2 [&::-webkit-slider-runnable-track]:rounded-sm [&::-webkit-slider-runnable-track]:border [&::-webkit-slider-runnable-track]:border-border [&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_right,var(--color-accent-strong)_0_var(--fill-pct,50%),var(--color-secondary)_var(--fill-pct,50%)_100%)] [&::-moz-range-track]:h-2 [&::-moz-range-track]:rounded-sm [&::-moz-range-track]:border [&::-moz-range-track]:border-border [&::-moz-range-track]:bg-[linear-gradient(to_right,var(--color-accent-strong)_0_var(--fill-pct,50%),var(--color-secondary)_var(--fill-pct,50%)_100%)] [&::-webkit-slider-thumb]:size-5 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-[4px] [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-foreground [&::-webkit-slider-thumb]:bg-primary [&::-webkit-slider-thumb]:mt-[-6px] [&::-moz-range-thumb]:size-5 [&::-moz-range-thumb]:rounded-[4px] [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-foreground [&::-moz-range-thumb]:bg-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
className
)}
{...props}
/>
);
}
);
Slider.displayName = "Slider";
export { Slider };
Props
| Prop | Type | Description |
|---|---|---|
| value | number | Controlled value. |
| defaultValue | number | Uncontrolled initial value (default 50). |
| min | number | Minimum value (default 0). |
| max | number | Maximum value (default 100). |
| step | number | Step increment (default 1). |
| onValueChange | (value: number) => void | Called when the value changes. |
| disabled | boolean | Disables interaction (default false). |