Elastic Slider

Slider with elastic rubber-band drag and magnetic snap feedback.

Last updated August 7, 2026

CreditsModifiedPublished

Copied from Josh Puckett — Interface Craft and Chánh Đại — Elastic Slider.

  • Value 0 now maps to the reading start (right edge in RTL) instead of always the physical left, matching a native <input type="range"> in RTL browsers
  • Arrow key direction follows reading direction: ArrowRight decreases the value in RTL instead of always increasing it
  • Fill, handle, hash marks, label, and value text use logical start/end positioning instead of left/right

Overview

"use client"
 
import { useState } from "react"
 
import { ElasticSlider } from "@/components/ui/elastic-slider"
 
export function ElasticSliderDemoExample() {
  const [opacity, setOpacity] = useState(0.5)
 
  return (
    <div className="flex w-50 flex-col gap-4">
      <ElasticSlider
        label="Opacity"
        min={0}
        max={1}
        value={opacity}
        onValueChange={setOpacity}
      />
 
      <ElasticSlider
        label="Blur"
        min={0}
        max={100}
        step={1}
        defaultValue={20}
        formatValue={(v) => `${v}px`}
      />
 
      <ElasticSlider
        label="Saturation"
        min={0}
        max={10}
        step={0.1}
        defaultValue={8}
      />
    </div>
  )
}

Installation

$ npx shadcn@latest add https://ui.persian-labs.ir/r/elastic-slider.json

Usage

import { ElasticSlider } from "@/components/elastic-slider"
 
const [opacity, setOpacity] = useState(0.5)
 
<ElasticSlider
  label="Opacity"
  min={0}
  max={1}
  value={opacity}
  onValueChange={setOpacity}
/>

Uncontrolled:

<ElasticSlider label="Opacity" min={0} max={1} defaultValue={0.5} />

Range slider

Need two handles on one track — a min and a max, like a price filter? Use Elastic Range Slider instead.

RTL

"use client"
 
import { useState } from "react"
 
import { ElasticSlider } from "@/components/ui/elastic-slider"
 
export function ElasticSliderRtlExample() {
  const [price, setPrice] = useState(50)
 
  return (
    <div className="w-50">
      <ElasticSlider
        label="قیمت"
        min={0}
        max={100}
        step={1}
        value={price}
        onValueChange={setPrice}
        formatValue={(v) => `${v} هزار تومان`}
      />
    </div>
  )
}

Keyboard

Keys

PropTypeDefaultDescription
ArrowRight / ArrowUpIncrease by one step. ArrowRight decreases instead in RTL.
ArrowLeft / ArrowDownDecrease by one step. ArrowLeft increases instead in RTL.
Shift + ArrowIncrease or decrease by step × 10.
HomeJump to min.
EndJump to max.

API Reference

ElasticSlider

PropTypeDefaultDescription
labelstringrequiredLabel shown inside the track.
valuenumberundefinedControlled value. Use together with onValueChange.
defaultValuenumberminInitial value for uncontrolled mode.
onValueChange(value: number) => voidCalled with the new value on drag, click, or key press.
minnumber0Minimum value.
maxnumber1Maximum value.
stepnumber0.01Smallest increment.
formatValue(value: number) => stringvalue.toFixed(...)Format the displayed value, based on step's decimal precision by default.
aria-labelstringlabelAccessible name. Falls back to label.