bigbullui logobigbullui
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 slider

Or install the full npm package:

npm install bigbullui

Usage

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

<Slider defaultValue={60} aria-label="Volume" />

Source code

slider.tsx

Zero 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

PropTypeDescription
valuenumberControlled value.
defaultValuenumberUncontrolled initial value (default 50).
minnumberMinimum value (default 0).
maxnumberMaximum value (default 100).
stepnumberStep increment (default 1).
onValueChange(value: number) => voidCalled when the value changes.
disabledbooleanDisables interaction (default false).