Range Slider Range Slider
beui/range-sliderFreeComponent
Tick dots, and a vertical-bar thumb that bounces as it lands on each step.
Install it
npx shadcn@latest add https://beui.dev/r/range-slider.jsonSource
1"use client";2// beui.dev/components/motion/range-slider34import {5 motion,6 useMotionTemplate,7 useMotionValue,8 useReducedMotion,9 useSpring,10 useTransform,11} from "motion/react";12import { useEffect } from "react";1314import { SPRING_GLIDE } from "@/lib/ease";15import { type SliderOptions, useSlider } from "@/lib/hooks/use-slider";16import { cn } from "@/lib/utils";1718// Bouncy grab feedback for the thumb scale only.19const SPRING_BOUNCY = { type: "spring", stiffness: 500, damping: 14, mass: 0.7 } as const;2021export interface RangeSliderProps extends SliderOptions {22 /** Render a tick dot at each step. */23 showTicks?: boolean;24 className?: string;25}2627export function RangeSlider({ showTicks = true, className, ...options }: RangeSliderProps) {28 const reduce = useReducedMotion();29 const { percent, dragging, min, max, step, trackProps, sliderProps } = useSlider(options);3031 // Spring-smoothed position drives both the thumb and the fill.32 const target = useMotionValue(percent);33 useEffect(() => {34 target.set(percent);35 }, [percent, target]);36 const smooth = useSpring(target, SPRING_GLIDE);37 const pos = reduce ? target : smooth;38 const left = useMotionTemplate`${pos}%`;39 // Self-offset the thumb from 0% (flush left) to -100% (flush right) of its40 // own width so it stays fully inside the track at both ends — no clip, no gap.41 const thumbX = useTransform(pos, (p) => `${-p}%`);4243 // Floor rather than round, so a range the step does not divide (0 to 10 by 4)44 // stops its dots at the last whole step instead of drawing one past max.45 // toFixed comes first because 0.3/0.1 is 2.9999999999999996, which would46 // floor to 2 and drop the last dot.47 const steps = Math.floor(Number(((max - min) / step).toFixed(6)));48 const ticks =49 showTicks && steps > 0 && steps <= 5050 ? Array.from({ length: steps + 1 }, (_, i) => Number((min + i * step).toFixed(6)))51 : [];5253 return (54 <div55 {...trackProps}56 className={cn(57 "relative flex h-10 w-full touch-none select-none items-center overflow-hidden rounded-lg bg-muted",58 options.disabled59 ? "pointer-events-none opacity-50"60 : "cursor-grab active:cursor-grabbing",61 className,62 )}63 >64 {/* fill — runs from the left edge to the thumb, consistent tone */}65 <motion.div className="absolute inset-y-0 left-0 bg-foreground/15" style={{ width: left }} />6667 {/* Ticks, inset by half the thumb's width. That inset is the span the68// … truncated
What it pulls in
npm packages
Files it writes
More from beui
All 104 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Action Swap Bluraction-swap-blur | beui | Components | Free | 3 deps · 4 files | |
| Action Swap Cascadeaction-swap-cascade | beui | Components | Free | 3 deps · 4 files | |
| Action Swap Rollaction-swap-roll | beui | Components | Free | 3 deps · 4 files | |
| Agent Activityagent-activity | beui | Components | Free | 4 deps · 9 files | |
| Agent Loading States Agent Progressagent-progress | beui | Components | Free | 3 deps · 3 files | |
| AI Sidebarai-sidebar | beui | Components | Free | 4 deps · 5 files | |
| Animated Badgeanimated-badge | beui | Components | Free | 4 deps · 3 files | |
| Number Animation Animated Numberanimated-number | beui | Components | Free | 3 deps · 3 files |
