Skip to content
Lenso UI

Slider

A slider allows a user to select one or more values within a range

Usage

import { Slider } from '@lenso/ui';
"use client";/** Adapted from HeroUI v3.2.6. SPDX-License-Identifier: Apache-2.0 */import { Slider } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";
const styles = stylex.create({ slider: { width: "100%", maxWidth: 320 } });export function Default() {  return (    <Slider xstyle={styles.slider} defaultValue={30}>      <Slider.Label>Volume</Slider.Label>      <Slider.Output />      <Slider.Control>        <Slider.Track>          <Slider.Fill />        </Slider.Track>        <Slider.Thumb aria-label="Volume" />      </Slider.Control>    </Slider>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

Anatomy

import { Slider, Label } from '@lenso/ui';
export default () => (  <Slider>    <Label />    <Slider.Output />    <Slider.Track>      <Slider.Fill />      <Slider.Thumb />    </Slider.Track>  </Slider>)

Examples

Disabled

"use client";/** Adapted from HeroUI v3.2.6. SPDX-License-Identifier: Apache-2.0 */import { Slider } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({ root: { width: "100%", maxWidth: "20rem" } });export function Disabled() {  return (    <Slider disabled xstyle={styles.root} defaultValue={30}>      <Slider.Label>Volume</Slider.Label>      <Slider.Output />      <Slider.Control>        <Slider.Track>          <Slider.Fill />        </Slider.Track>        <Slider.Thumb />      </Slider.Control>    </Slider>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

Range Slider Anatomy

import { Slider, Label } from '@lenso/ui';
export default () => (  <Slider defaultValue={[25, 75]}>    <Label />    <Slider.Output />    <Slider.Track>      {({state}) => (        <>          <Slider.Fill />          {state.values.map((_, i) => (            <Slider.Thumb key={i} index={i} />          ))}        </>      )}    </Slider.Track>  </Slider>)

Vertical

"use client";/** Adapted from HeroUI v3.2.6. SPDX-License-Identifier: Apache-2.0 */import { Slider } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({  wrapper: { display: "flex", height: "16rem", alignItems: "center", justifyContent: "center" },  root: { height: "100%" },});export function Vertical() {  return (    <div {...stylex.props(styles.wrapper)}>      <Slider xstyle={styles.root} defaultValue={30} orientation="vertical">        <Slider.Label>Volume</Slider.Label>        <Slider.Output />        <Slider.Control>          <Slider.Track>            <Slider.Fill />          </Slider.Track>          <Slider.Thumb />        </Slider.Control>      </Slider>    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

Range

"use client";/** * Adapted from HeroUI v3.2.6. SPDX-License-Identifier: Apache-2.0 * Native Base UI control geometry and value formatting. */import { Slider } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";
const styles = stylex.create({ root: { width: "100%", maxWidth: "20rem" } });
export function Range() {  return (    <Slider      xstyle={styles.root}      defaultValue={[100, 500]}      format={{ currency: "USD", style: "currency" }}      max={1000}      min={0}      step={50}    >      <Slider.Label>Price Range</Slider.Label>      <Slider.Output>{(formattedValues) => formattedValues.join(" – ")}</Slider.Output>      <Slider.Control>        <Slider.Track>          <Slider.Fill />        </Slider.Track>        <Slider.Thumb index={0} aria-label="Minimum price" />        <Slider.Thumb index={1} aria-label="Maximum price" />      </Slider.Control>    </Slider>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

Render Function

"use client";/** Adapted from HeroUI v3.2.6. SPDX-License-Identifier: Apache-2.0 */import { Slider } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({ root: { width: "100%", maxWidth: "20rem" } });export function RenderFunction() {  return (    <Slider      xstyle={styles.root}      defaultValue={30}      render={(props) => <div {...props} data-custom="foo" />}    >      <Slider.Label>Volume</Slider.Label>      <Slider.Output />      <Slider.Control>        <Slider.Track>          <Slider.Fill />        </Slider.Track>        <Slider.Thumb />      </Slider.Control>    </Slider>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

Customization

Tailwind CSS

"use client";/** Adapted from HeroUI v3.2.6. SPDX-License-Identifier: Apache-2.0 */import { Slider } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({  root: { width: "100%", maxWidth: "20rem" },  label: { fontWeight: 500, color: "var(--foreground)" },  output: { fontSize: ".75rem", color: "var(--muted)", fontVariantNumeric: "tabular-nums" },  track: { backgroundColor: "var(--default)" },  fill: { backgroundColor: "var(--accent)" },  thumb: {    backgroundColor: "var(--accent)",    "::after": { backgroundColor: "var(--accent-foreground)" },  },});export function CustomStyles() {  return (    <Slider xstyle={styles.root} defaultValue={40}>      <Slider.Label xstyle={styles.label}>Brightness</Slider.Label>      <Slider.Output xstyle={styles.output} />      <Slider.Control>        <Slider.Track xstyle={styles.track}>          <Slider.Fill xstyle={styles.fill} />        </Slider.Track>        <Slider.Thumb xstyle={styles.thumb} />      </Slider.Control>    </Slider>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

Global CSS

To customize the Slider component classes, you can use the @layer components directive. Learn more.

@layer components {  .slider {    @apply flex flex-col gap-2;  }
  .slider__output {    @apply text-muted-fg text-sm;  }
  .slider-track {    @apply relative h-2 w-full rounded-full bg-surface-secondary;  }
  .slider-fill {    @apply absolute h-full rounded-full bg-accent;  }
  .slider-thumb {    @apply size-4 rounded-full bg-accent border-2 border-background;  }}

Styling Reference

HeroUI follows the BEM methodology to ensure component variants and states are reusable and easy to customize.

CSS Classes

The Slider component uses these CSS classes (View source styles):

Base Classes [!toc]

  • .slider - Base slider container
  • .slider__output - Output element displaying current value(s)
  • .slider-track - Track element containing fill and thumbs
  • .slider-fill - Fill element showing selected range
  • .slider-thumb - Individual thumb element

State Classes [!toc]

  • .slider[data-disabled="true"] - Disabled slider state
  • .slider[data-orientation="vertical"] - Vertical orientation
  • .slider-thumb[data-dragging="true"] - Thumb being dragged
  • .slider-thumb[data-focus-visible="true"] - Thumb keyboard focused
  • .slider-thumb[data-disabled="true"] - Disabled thumb state
  • .slider-track[data-fill-start="true"] - Fill starts at beginning
  • .slider-track[data-fill-end="true"] - Fill ends at end

Interactive States

The component supports both CSS pseudo-classes and data attributes for flexibility:

  • Hover: :hover or [data-hovered="true"] on thumb
  • Focus: :focus-visible or [data-focus-visible="true"] on thumb
  • Dragging: [data-dragging="true"] on thumb
  • Disabled: :disabled or [data-disabled="true"] on slider or thumb

API Reference

Slider

PropTypeDefaultDescription
valuenumber | number[]-The current value (controlled)
defaultValuenumber | number[]-The default value (uncontrolled)
onChange(value: number | number[]) => void-Handler called when the value changes
onChangeEnd(value: number | number[]) => void-Handler called when dragging ends
minValuenumber0The slider's minimum value
maxValuenumber100The slider's maximum value
stepnumber1The slider's step value
formatOptionsIntl.NumberFormatOptions-The display format of the value label
orientation"horizontal" | "vertical""horizontal"The orientation of the slider
isDisabledboolean-Whether the slider is disabled
aria-labelstring-Accessibility label for the slider
aria-labelledbystring-ID of element that labels the slider
classNamestring-Additional CSS classes
childrenReactNode | RenderFunction-Slider content or render function
renderDOMRenderFunction<keyof React.JSX.IntrinsicElements, SliderRenderProps>-Overrides the default DOM element with a custom render function.

Slider.Output

PropTypeDefaultDescription
classNamestring-Additional CSS classes
childrenReactNode | RenderFunction-Output content or render function
renderDOMRenderFunction<keyof React.JSX.IntrinsicElements, SliderOutputRenderProps>-Overrides the default DOM element with a custom render function.

Slider.Track

PropTypeDefaultDescription
classNamestring-Additional CSS classes
childrenReactNode | RenderFunction-Track content or render function
renderDOMRenderFunction<keyof React.JSX.IntrinsicElements, SliderTrackRenderProps>-Overrides the default DOM element with a custom render function.

Slider.Fill

PropTypeDefaultDescription
classNamestring-Additional CSS classes
styleCSSProperties-Inline styles

Slider.Thumb

PropTypeDefaultDescription
indexnumber0Index of the thumb within the slider
isDisabledboolean-Whether this thumb is disabled
namestring-The name of the input element, used when submitting an HTML form
classNamestring-Additional CSS classes
childrenReactNode | RenderFunction-Thumb content or render function
renderDOMRenderFunction<keyof React.JSX.IntrinsicElements, SliderThumbRenderProps>-Overrides the default DOM element with a custom render function.

Render Props

When using render functions with Slider.Output or Slider.Track, these values are provided:

PropTypeDescription
stateSliderStateThe state of the slider
valuesnumber[]Values managed by the slider by thumb index
getThumbValueLabel(index: number) => stringReturns the string label for the specified thumb's value
orientation"horizontal" | "vertical"The orientation of the slider
isDisabledbooleanWhether the slider is disabled

Examples

Basic Usage

import { Slider, Label } from '@lenso/ui';
<Slider defaultValue={30}>  <Label>Volume</Label>  <Slider.Output />  <Slider.Track>    <Slider.Fill />    <Slider.Thumb />  </Slider.Track></Slider>

Range Slider

import { Slider, Label } from '@lenso/ui';
<Slider  defaultValue={[100, 500]}  formatOptions={{style: "currency", currency: "USD"}}  maxValue={1000}  minValue={0}  step={50}>  <Label>Price Range</Label>  <Slider.Output />  <Slider.Track>    {({state}) => (      <>        <Slider.Fill />        {state.values.map((_, i) => (          <Slider.Thumb key={i} index={i} />        ))}      </>    )}  </Slider.Track></Slider>

Controlled Value

import { Slider, Label } from '@lenso/ui';import { useState } from 'react';
function ControlledSlider() {  const [value, setValue] = useState(25);
  return (    <>      <Slider value={value} onChange={setValue}>        <Label>Volume</Label>        <Slider.Output />        <Slider.Track>          <Slider.Fill />          <Slider.Thumb />        </Slider.Track>      </Slider>      <p>Current value: {value}</p>    </>  );}

Custom Value Formatting

import { Slider, Label } from '@lenso/ui';
<Slider  defaultValue={60}  formatOptions={{style: "currency", currency: "USD"}}>  <Label>Price</Label>  <Slider.Output />  <Slider.Track>    <Slider.Fill />    <Slider.Thumb />  </Slider.Track></Slider>

Vertical Orientation

import { Slider, Label } from '@lenso/ui';
<Slider defaultValue={30} orientation="vertical" aria-label="Volume">  <Label>Volume</Label>  <Slider.Output />  <Slider.Track>    <Slider.Fill />    <Slider.Thumb />  </Slider.Track></Slider>

Custom Output Display

import { Slider, Label } from '@lenso/ui';
<Slider defaultValue={[25, 75]}>  <Label>Range</Label>  <Slider.Output>    {({state}) =>      state.values.map((_, i) => state.getThumbValueLabel(i)).join(' – ')    }  </Slider.Output>  <Slider.Track>    {({state}) => (      <>        <Slider.Fill />        {state.values.map((_, i) => (          <Slider.Thumb key={i} index={i} />        ))}      </>    )}  </Slider.Track></Slider>

Accessibility

The Slider component implements the ARIA slider pattern and provides:

  • Full keyboard navigation support (Arrow keys, Home, End, Page Up/Down)
  • Screen reader announcements for value changes
  • Proper focus management
  • Support for disabled states
  • HTML form integration via hidden input elements
  • Internationalization support with locale-aware value formatting
  • Right-to-left (RTL) language support

For more information, see the React Aria Slider documentation.