Siteplane UI

Slider

Choose a numeric value or range by moving one or more thumbs.

Markdown
40
"use client";

import { Field, FieldLabel } from "@/siteplane/components/field";
import { Slider, SliderValue } from "@/siteplane/components/slider";

export default function Example() {
  return (
    <Field className="w-full max-w-sm">
      <Slider defaultValue={40}>
        <div className="flex items-center justify-between gap-2">
          <FieldLabel>Volume</FieldLabel>
          <SliderValue />
        </div>
      </Slider>
    </Field>
  );
}

Installation

pnpm dlx shadcn@4.18.0 add @siteplane/slider

Complete the one-time setup first. Registry JSON lists source files and dependencies.

For this page’s examples, also install:

pnpm dlx shadcn@4.18.0 add @siteplane/button @siteplane/form @siteplane/input

Field is installed with Input.

Usage

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

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

Composition

Slider
├── SliderValue (optional child)
└── built-in control, track and thumbs

Controlled range

An array creates one thumb per value. The application owns the selected range.

200 – 800
"use client";

import { Field, FieldLabel } from "@/siteplane/components/field";
import { Slider, SliderValue } from "@/siteplane/components/slider";
import { useState } from "react";

export default function Example() {
  const [value, setValue] = useState<number | readonly number[]>([200, 800]);
  return (
    <Field className="w-full max-w-sm">
      <Slider
        max={1000}
        min={0}
        onValueChange={setValue}
        step={50}
        value={value}
      >
        <div className="flex items-center justify-between gap-2">
          <FieldLabel>Price range</FieldLabel>
          <SliderValue />
        </div>
      </Slider>
    </Field>
  );
}

Scale labels

Use labels to mark positions on the scale; they sit below the track and are hidden for vertical sliders. Each label stays on one line, and labels at the ends stay within the slider’s edges. They don’t name the slider, so still give it a label.

5 GB20 GB35 GB
"use client";

import { Field, FieldLabel } from "@/siteplane/components/field";
import { Slider } from "@/siteplane/components/slider";

export default function Example() {
  return (
    <Field className="w-full max-w-sm">
      <FieldLabel>Storage limit</FieldLabel>
      <Slider
        defaultValue={15}
        labels={[
          { value: 5, label: "5 GB" },
          { value: 20, label: "20 GB" },
          { value: 35, label: "35 GB" },
        ]}
        max={35}
        min={5}
      />
    </Field>
  );
}

Orientation and disabled

Vertical controls need enough space for their minimum height.

"use client";

import { Field, FieldLabel } from "@/siteplane/components/field";
import { Slider } from "@/siteplane/components/slider";

export default function Example() {
  return (
    <div className="grid w-full max-w-sm gap-4">
      <Field>
        <FieldLabel>Volume, vertical</FieldLabel>
        <Slider defaultValue={40} orientation="vertical" />
      </Field>
      <Field disabled>
        <FieldLabel>Volume, disabled</FieldLabel>
        <Slider defaultValue={40} />
      </Field>
    </div>
  );
}

Sizes

Omit size to inherit the provider's control size. The track and thumbs grow with the size; the value and the scale labels keep 12px text at every size.

"use client";

import { Field, FieldLabel } from "@/siteplane/components/field";
import { Slider } from "@/siteplane/components/slider";

const sizes = [
  { name: "small", size: "sm" },
  { name: "medium", size: "md" },
  { name: "large", size: "lg" },
  { name: "extra large", size: "xl" },
] as const;

export default function Example() {
  return (
    <div className="grid w-full max-w-sm gap-4">
      {sizes.map(({ name, size }) => (
        <Field key={size}>
          <FieldLabel>Volume, {name}</FieldLabel>
          <Slider defaultValue={40} size={size} />
        </Field>
      ))}
    </div>
  );
}

Form integration

Give the surrounding Field a name to submit the slider value with the form; a range submits one entry per thumb.

30

Minutes of inactivity before members are signed out.

"use client";

import { Button } from "@/siteplane/components/button";
import {
  Field,
  FieldDescription,
  FieldLabel,
} from "@/siteplane/components/field";
import { Form } from "@/siteplane/components/form";
import { Slider, SliderValue } from "@/siteplane/components/slider";

export default function Example() {
  return (
    <Form
      className="grid w-full max-w-sm gap-3"
      onSubmit={(event) => {
        event.preventDefault();
      }}
    >
      <Field name="sessionTimeout">
        <Slider defaultValue={30} max={120} min={15} step={15}>
          <div className="flex items-center justify-between gap-2">
            <FieldLabel>Session timeout</FieldLabel>
            <SliderValue />
          </div>
        </Slider>
        <FieldDescription>
          Minutes of inactivity before members are signed out.
        </FieldDescription>
      </Field>
      <Button type="submit">Save</Button>
    </Form>
  );
}

API reference

Component properties
PropTypeDefaultDescription
Slider.sizesm | md | lg | xlprovider / lgControl, track, thumb and label sizes.
Slider.labelsreadonly {label: ReactNode; value: number}[]unsetHorizontal scale labels on one line; positions clamp to the range and end labels stay within the slider.
Slider.value / defaultValuenumber | number[]unset (one thumb at min)Controlled or initial values; one thumb per value.
Slider.min / maxnumber0 / 100Range bounds.
Slider.orientationhorizontal | verticalhorizontalControl direction.
Slider.thumbAlignmentfoundation alignmentedgeForwarded override can replace the edge default.
Slider.aria-labelstringunsetApplied to every generated thumb.
Slider.onValueChange(value, eventDetails) => voidunsetLive edit callback.
Slider.onValueCommitted(value, eventDetails) => voidunsetCommitted edit callback.
SliderValue.sizesm | md | lg | xlroot / provider / lgSize context of the value. Its text is 12px at every size, like the scale labels.

SliderProps, SliderValueProps and SliderPrimitive are exported. Slider always renders its own control/track/indicator/thumbs; children supplement them, not replace them. SliderValue formats the current value using the Base UI Slider Value contract. Other step, form, disabled, keyboard and validation props are inherited. The wrapper does not expose a separate label per thumb; the supplied aria-label is repeated. Scale labels are not public components.

Read the installed source.

Usage notes

Arrow keys adjust the focused thumb; Home/End move to bounds. Do not use scale labels as the only accessible name. Use NumberField if precise typed entry is necessary. The application owns business validation and persistence.