Single or dual thumb scrubber with marks, orientation, label, and value chrome. Prefer Number Slider for a compact settings row, and Tick Slider for bound labels with a tick gauge.
Also known as: range slider, dual slider, range scrubber, marked slider, continuous slider
Install with the CLI, or copy the component source into your project.
Add slider (npm)
1npx --yes -p @default-file/ui df-ui add slider
Single value on a 0 to 100 bar track.
Line track with a leading icon, label, and percent readout above the track. Use valuePosition footer to move that row below.
valuePosition footer places the label and value row under the track. header keeps them above.
valuePosition start places the label and value beside the track on the left. end places them on the right.
valuePosition ends puts the label at the start and the value at the end. ends-reverse flips them.
formatValue replaces the built in number or percent readout.
scrubSound plays a smooth tone while dragging. Pitch follows position and level follows drag speed. Off by default.
valueSlot replaces the default readout with any node. Pass a function to receive the current values and compose a Badge, counter, or other control. Placement still follows valuePosition.
Two thumbs on a line track. Pass two values to enable range mode. Readout shows low to high.
Dual thumbs with minStepsBetween keeping a minimum gap. Optional description can explain the constraint; omit it to hide the copy.
Line track with the value in a bubble above the thumb. Label stays in the header.
Dual thumbs with currency values under each handle. valuePosition thumbs keeps the label in the header.
Outline icon buttons beside a line track. stepButtonSteps sets how many steps each click moves; here it is 5.
Bar and line vertical dual thumb ranges side by side. Value stack sits under the track. Minimum sits at the bottom.
Default keeps label above value with marks on the end. The line example uses value-first and marksSide start to flip the stack and sides.
Snaps to evenly spaced stops under a line track. Header shows the label and live value.
thickness scales the track and thumb. sm is fine, md is default, lg is a thick rail.
Root disabled state. Same treatment for bar or line, range, marks, bubble, and step buttons.
Single or dual thumb scrubber with bar or line chrome, horizontal or vertical orientation, and optional label, leading icon, and value readout. Prefer Number Slider for a compact settings row, and Tick Slider for bound labels with a tick gauge. Without marks, the thumb tracks the pointer smoothly while dragging and commits to step. With marks, the value snaps to the nearest mark.
| Prop | Type | Default | Notes |
|---|---|---|---|
| min | number | 0 | Minimum value. |
| max | number | 100 | Maximum value. |
| step | number | 1 | Increment between committed values when marks are omitted. Values of 0 or less fall back to 1. With marks, step still controls readout precision unless formatValue is set. |
| value | number[] | - | Controlled value. One entry is a single thumb. Two entries enable a range with minimum and maximum thumbs. Values are snapped to step, ordered low to high, and clamped. |
| defaultValue | number[] | [min] | Uncontrolled initial value. Pass two numbers for a range slider. |
| onValueChange | (value: number[]) => void | - | Called when a committed value changes. Receives one or two stepped values. |
| variant | "bar" | "line" | "bar" | Track chrome. bar is a filled pill track. line is a thin mute rail with a thicker active segment and circular thumbs. |
| orientation | "horizontal" | "vertical" | "horizontal" | Track direction. vertical places the minimum at the bottom and stacks the range readout under the track. |
| thickness | "sm" | "md" | "lg" | "md" | Track and thumb thickness. sm is fine, md is the default, lg is a thick rail. Remaps the slider thickness tokens on the root. Style overrides of those variables still win. |
| minStepsBetween | number | 0 | Minimum gap between thumbs in range mode, measured in steps (value gap is minStepsBetween times step). With marks, the gap is counted in mark indices. Use with description to explain the constraint. |
| marks | Array<number | { value: number; label?: React.ReactNode }> | - | Discrete snap stops. When set, dragging and keyboard move between marks only. Labels render beside a vertical track or under a horizontal track. Omit label to show the formatted value. Prefer a stable array reference. |
| valuePosition | "auto" | "header" | "footer" | "start" | "end" | "ends" | "ends-reverse" | "thumb" | "thumbs" | "bubble" | "auto" | Where the label and value chrome sit. header is above the track. footer is below the track with the same label and value row. start places both beside the track at the start. end places both at the end. ends puts the label at the start and the value at the end, with the track between them. ends-reverse flips that (value at the start, label at the end). auto uses thumb for a vertical marked single thumb, footer for other vertical sliders, and header for horizontal. thumb is vertical single-thumb only; on horizontal or range it falls back to header. thumbs places a formatted value under each thumb and keeps label, leading, and description in the header. bubble floats a tooltip on a single thumb and keeps label, leading, and description in the header. |
| thumbValueOrder | "label-first" | "value-first" | "label-first" | Stack order for traveling thumb meta when valuePosition is thumb. label-first puts the label above the value. value-first puts the value above the label. |
| marksSide | "start" | "end" | "end" | Which side mark labels sit on for a vertical slider. end keeps marks on the right and thumb meta on the left. start swaps them. Ignored for horizontal marks, which stay under the track. |
| label | React.ReactNode | - | Optional heading. In the header by default. For vertical marked sliders it travels with the thumb when valuePosition is thumb or auto. |
| description | React.ReactNode | - | Optional supporting line under the label. Omit it to hide the copy while keeping constraints such as minStepsBetween. |
| leading | React.ReactNode | - | Optional leading content before the label, such as an icon. Travels with the thumb when valuePosition is thumb. |
| showStepButtons | boolean | false | Show outline icon buttons that decrease and increase the active thumb. Distance per click follows stepButtonSteps. Keyboard arrows still move one step. In range mode, buttons follow the last focused or dragged thumb. |
| stepButtonSteps | number | 1 | How many step increments each step button applies. With step 1, 5 moves by 5. With marks, hops that many mark stops. Does not change keyboard step size. Values below 1 are treated as 1. |
| showValue | boolean | - | Show the value slot. Defaults to true when label, description, leading, valueSlot, formatValue, or formatValues is set. Set false to keep a label without a readout. Placement follows valuePosition. |
| valueFormat | "number" | "percent" | "number" | Built in readout format when valueSlot, formatValue, and formatValues are omitted. number uses step precision. percent shows position across min to max. |
| valueSlot | React.ReactNode | ((values: number[]) => React.ReactNode) | - | Custom value slot for header, footer, start, end, ends, ends-reverse, and thumb chrome. Replaces the default readout with any node, such as a Badge or counter. Pass a render function to receive the current values and keep the slot in sync. Does not replace per-thumb thumbs or bubble readouts. Overrides formatValues, formatValue, and valueFormat for display. Keyboard and aria valuetext still use formatValue or valueFormat. |
| formatValue | (value: number) => string | - | Formats each thumb value for the default readout, per-thumb thumbs and bubble text, and aria valuetext. Overrides valueFormat. Ignored for chrome display when valueSlot is set. |
| formatValues | (values: number[]) => React.ReactNode | - | Custom readout for the full value array when valueSlot is omitted. String and number results keep the default value typography. Other React nodes are treated as custom slot content. Prefer valueSlot for a dedicated Badge or counter. |
| scrubSound | boolean | false | Play a smooth scrub tone while dragging a thumb with the pointer. Pitch follows position and level follows drag speed. Off by default. Stops on release, unmount, or when the document is hidden. Skipped when reduced motion is preferred. Separate from the Tick Slider step tick. |
| disabled | boolean | - | Blocks pointer, keyboard, marks, and step buttons for every variant and layout. |
| aria-label | string | - | Accessible name. For a range, each thumb appends Minimum or Maximum. |
| aria-labelledby | string | - | ID of a labeling element. Defaults to the label element when label is set. |
| className | string | - | Additional styles on the root. |
| style | React.CSSProperties | - | Inline styles on the root. Can also override Slider CSS variables directly. |
| ref | React.Ref<HTMLDivElement> | - | Ref to the root element. |
thickness remaps track, line, and thumb variables to the sm or lg companions. variant line uses the line thickness tokens. valuePosition bubble uses the bubble tokens and reserves clearance on the rail or control. valuePosition start, end, ends, and ends-reverse place chrome in a track row beside the rail using --df-slider-step-gap. orientation vertical uses --df-slider-vertical-size for track length and --df-slider-vertical-column-gap between meta, track, and marks. Horizontal marks use --df-slider-marks-gap. Prop and style overrides still win.
| Token | Type | Default | Notes |
|---|---|---|---|
| --primary | CSS color | var(--df-neutral-850, var(--df-neutral-900)) | Active fill (both variants) and bar thumb border. |
| --foreground | CSS color | var(--df-neutral-900) | Label text, leading icon, and line thumb border. |
| --muted | CSS color | var(--df-neutral-50) | Bar track background. |
| --muted-foreground | CSS color | var(--df-neutral-500) | Value readout text. |
| --background | CSS color | var(--df-neutral-0) | Thumb fill. |
| --df-slider-track-height | CSS length | calc(3 * var(--spacing-unit)) | Bar track thickness for thickness md. |
| --df-slider-line-track-height | CSS length | calc(1 * var(--spacing-unit)) | Line mute rail thickness for thickness md. |
| --df-slider-line-range-height | CSS length | calc(2 * var(--spacing-unit)) | Line active segment thickness for thickness md. |
| --df-slider-line-track-bg | CSS color | color-mix(in oklch, var(--foreground) 16%, transparent) | Mute rail fill for the line variant. |
| --df-slider-thumb-size | CSS length | var(--df-affordance-size-sm) | Thumb size for thickness md. |
| --df-slider-track-height-sm | CSS length | calc(2 * var(--spacing-unit)) | Bar track thickness for thickness sm. |
| --df-slider-line-track-height-sm | CSS length | calc(0.5 * var(--spacing-unit)) | Line mute rail thickness for thickness sm. |
| --df-slider-line-range-height-sm | CSS length | calc(1 * var(--spacing-unit)) | Line active segment thickness for thickness sm. |
| --df-slider-thumb-size-sm | CSS length | var(--df-affordance-size-xs) | Thumb size for thickness sm. |
| --df-slider-track-height-lg | CSS length | calc(5 * var(--spacing-unit)) | Bar track thickness for thickness lg. |
| --df-slider-line-track-height-lg | CSS length | calc(2 * var(--spacing-unit)) | Line mute rail thickness for thickness lg. |
| --df-slider-line-range-height-lg | CSS length | calc(4 * var(--spacing-unit)) | Line active segment thickness for thickness lg. |
| --df-slider-thumb-size-lg | CSS length | var(--df-affordance-size-md) | Thumb size for thickness lg. |
| --df-slider-vertical-size | CSS length | calc(48 * var(--spacing-unit)) | Vertical track length. |
| --df-slider-marks-gap | CSS length | calc(2 * var(--spacing-unit)) | Gap between the horizontal track and the marks row. |
| --df-slider-vertical-column-gap | CSS length | calc(5 * var(--spacing-unit)) | Gap between thumb meta, the vertical track, and mark labels. |
| --df-slider-marks-track-height | CSS length | calc(4 * var(--spacing-unit)) | Height of the horizontal marks row. |
| --df-slider-vertical-marks-min-width | CSS length | calc(10 * var(--spacing-unit)) | Minimum width of the vertical marks column. |
| --df-slider-bubble-pad-inline | CSS length | calc(2 * var(--spacing-unit)) | Horizontal padding inside the value bubble. |
| --df-slider-bubble-pad-block | CSS length | calc(0.75 * var(--spacing-unit)) | Vertical padding inside the value bubble. |
| --df-slider-bubble-offset | CSS length | calc(2 * var(--spacing-unit)) | Gap between the thumb and the value bubble. |
| --df-slider-bubble-radius | CSS length | var(--radius-md) | Corner radius of the value bubble. |
| --df-slider-bubble-clearance | CSS length | calc(var(--df-slider-thumb-size) + var(--df-slider-bubble-offset) + var(--df-text-11) + (2 * var(--df-slider-bubble-pad-block))) | Space reserved above or beside the track for the value bubble. Scales with thumb size, bubble padding, and type size. |
| --df-slider-thumb-value-clearance | CSS length | calc(5 * var(--spacing-unit)) | Height reserved under a horizontal track for per-thumb values. |
| --df-slider-thumb-value-column | CSS length | calc(12 * var(--spacing-unit)) | Width reserved beside a vertical track for per-thumb values. |
| --df-slider-thumb-value-gap | CSS length | calc(1.5 * var(--spacing-unit)) | Gap between the track and per-thumb values. |
| --df-slider-step-gap | CSS length | calc(2.5 * var(--spacing-unit)) | Gap between step buttons and the track. |
| --df-slider-header-gap | CSS length | calc(2 * var(--spacing-unit)) | Gap between header, track, and footer. |
| --df-slider-header-inline-gap | CSS length | calc(2 * var(--spacing-unit)) | Gap between the heading block and the header value. |
| --df-slider-tight-gap | CSS length | calc(0.5 * var(--spacing-unit)) | Tight stack gap for heading block, value stack, and thumb meta. |
| --df-slider-value-sep-mix | CSS percentage | 72% | Mix amount for the range separator against muted foreground. |
| --df-slider-label-gap | CSS length | calc(1.5 * var(--spacing-unit)) | Gap between leading content and the label. |
| --df-slider-leading-size | CSS length | calc(4 * var(--spacing-unit)) | Leading icon box size. |