HextaUI

Slider

Wähle einen Wert oder Bereich durch Ziehen, Tippen auf die Spur oder mit den Tasten, mit gleitenden Sprüngen und einer optionalen Wertblase.

Volume
60
import { Slider, SliderLabel, SliderValue } from "@/components/ui/slider"

export function SliderDemo() {
  return (
    <Slider defaultValue={60} className="max-w-xs">
      <SliderLabel>Volume</SliderLabel>
      <SliderValue />
    </Slider>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/slider.json

Fügt die Komponente, die HextaUI-Theme-Tokens und alle HextaUI-Komponenten hinzu, von denen sie abhängt.

import { Slider, SliderLabel, SliderValue } from "@/components/ui/slider"
<Slider defaultValue={[50]} max={100} step={1} aria-label="Volume" />

<Slider defaultValue={50}>
  <SliderLabel>Volume</SliderLabel>
  <SliderValue />
</Slider>

<Slider /> zeichnet einen Thumb pro Wert, sodass eine Zahl oder ein Array mit einem Element einen einzelnen Slider ergibt und zwei Elemente einen Bereich. Kinder wie <SliderLabel /> und <SliderValue /> sitzen in einer Zeile über der Spur.

Beim Ziehen folgt der Thumb dem Zeiger exakt. Springt der Wert, durch einen Druck auf die Spur, eine Taste oder einen neuen kontrollierten Wert, gleiten Thumb und Füllung in 180ms dorthin, statt zu teleportieren.

Slider
├── SliderLabel
└── SliderValue

Größen

sm, default und lg skalieren Thumb und Spur gemeinsam.

import { Slider } from "@/components/ui/slider"

export function SliderSizes() {
  return (
    <div className="flex w-full max-w-xs flex-col gap-8">
      <Slider size="sm" defaultValue={25} aria-label="Small" />
      <Slider defaultValue={50} aria-label="Default" />
      <Slider size="lg" defaultValue={75} aria-label="Large" />
    </div>
  )
}

Range

Übergib zwei Werte für einen Bereich. Benenne jeden Thumb mit getAriaLabel und halte sie mit minStepsBetweenValues auseinander. Mit draggableRange ziehst du irgendwo zwischen den Thumbs, um beide gleichzeitig zu bewegen; ein Tippen dort bewegt weiterhin den nächsten Thumb.

"use client"

import { Slider, SliderLabel, SliderValue } from "@/components/ui/slider"

export function SliderRange() {
  return (
    <Slider
      defaultValue={[200, 800]}
      max={1000}
      step={10}
      minStepsBetweenValues={10}
      draggableRange
      format={{ style: "currency", currency: "USD", maximumFractionDigits: 0 }}
      getAriaLabel={(index) =>
        index === 0 ? "Minimum price" : "Maximum price"
      }
      className="max-w-xs"
    >
      <SliderLabel>Price</SliderLabel>
      <SliderValue />
    </Slider>
  )
}

Wertblase

showValue zeigt den formatierten Wert über dem Thumb, während er gezogen oder per Tastatur fokussiert wird. Nutze es statt einer Label-Zeile, da die Blase in den Raum über der Spur aufsteigt.

import { Slider } from "@/components/ui/slider"

export function SliderValueBubble() {
  return (
    <div className="flex w-full max-w-xs flex-col gap-10 pt-8">
      <Slider
        defaultValue={0.4}
        max={1}
        step={0.01}
        format={{ style: "percent" }}
        showValue
        aria-label="Opacity"
      />
      <Slider defaultValue={[30, 70]} showValue aria-label="Range" />
    </div>
  )
}

Schritte und Format

step rastet den Wert ein, largeStep legt den Sprung für Page Up und Shift + Pfeil fest, und format formatiert jede Zahl, die der Slider zeigt oder ansagt.

import { Slider, SliderLabel, SliderValue } from "@/components/ui/slider"

export function SliderSteps() {
  return (
    <Slider
      defaultValue={1}
      min={0.5}
      max={2}
      step={0.25}
      largeStep={0.5}
      format={{ minimumFractionDigits: 2 }}
      className="max-w-xs"
    >
      <SliderLabel>Playback speed</SliderLabel>
      <SliderValue />
    </Slider>
  )
}

Kontrolliert

onValueChange feuert bei jeder Bewegung und onValueCommitted einmal beim Loslassen, was der Moment zum Speichern ist. Auch das Setzen des Werts von außen gleitet.

"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import { Slider, SliderLabel, SliderValue } from "@/components/ui/slider"

export function SliderControlled() {
  const [value, setValue] = React.useState(40)
  const [saved, setSaved] = React.useState(40)

  return (
    <div className="flex w-full max-w-xs flex-col gap-4">
      <Slider
        value={value}
        onValueChange={setValue}
        onValueCommitted={setSaved}
      >
        <SliderLabel>Brightness</SliderLabel>
        <SliderValue />
      </Slider>
      <div className="flex items-center justify-between gap-3">
        <span className="text-sm text-muted-foreground">Saved: {saved}</span>
        <div className="flex gap-2">
          <Button variant="outline" size="sm" onClick={() => setValue(0)}>
            Off
          </Button>
          <Button variant="outline" size="sm" onClick={() => setValue(100)}>
            Max
          </Button>
        </div>
      </div>
    </div>
  )
}

Vertikal

Mit orientation="vertical" füllt der Slider die Höhe seines Elternelements, und Hoch erhöht den Wert.

import { Slider } from "@/components/ui/slider"

const bands = [
  { label: "60 Hz", value: 70 },
  { label: "250 Hz", value: 45 },
  { label: "1 kHz", value: 55 },
  { label: "4 kHz", value: 80 },
  { label: "16 kHz", value: 35 },
]

export function SliderVertical() {
  return (
    <div className="flex h-48 gap-6">
      {bands.map((band) => (
        <div key={band.label} className="flex flex-col items-center gap-3">
          <Slider
            orientation="vertical"
            defaultValue={band.value}
            aria-label={band.label}
          />
          <span className="text-xs text-muted-foreground tabular-nums">
            {band.label}
          </span>
        </div>
      ))}
    </div>
  )
}

Mit Icons

Setze Icons neben den Slider in einer Flex-Zeile. Der Slider nimmt die restliche Breite.

import { IconVolume, IconVolume3 } from "@tabler/icons-react"

import { Slider } from "@/components/ui/slider"

export function SliderIcons() {
  return (
    <div className="flex w-full max-w-xs items-center gap-3 text-muted-foreground">
      <IconVolume3 className="size-4 shrink-0" aria-hidden />
      <Slider defaultValue={50} aria-label="Volume" />
      <IconVolume className="size-4 shrink-0" aria-hidden />
    </div>
  )
}

Deaktiviert

Der ganze Slider dimmt und ignoriert Zeiger- und Tastatureingaben.

import { Slider, SliderLabel, SliderValue } from "@/components/ui/slider"

export function SliderDisabled() {
  return (
    <Slider defaultValue={30} disabled className="max-w-xs">
      <SliderLabel>Bass boost</SliderLabel>
      <SliderValue />
    </Slider>
  )
}

Lange Labels

Lange Labels umbrechen, und der Wert bleibt am Ende.

import { Slider, SliderLabel, SliderValue } from "@/components/ui/slider"

export function SliderLongLabel() {
  return (
    <Slider
      defaultValue={[1200, 48000]}
      min={0}
      max={50000}
      step={100}
      format={{ style: "currency", currency: "USD", maximumFractionDigits: 0 }}
      className="max-w-xs"
    >
      <SliderLabel>
        Annual household income before taxes, including side projects
      </SliderLabel>
      <SliderValue />
    </Slider>
  )
}

Rechts nach links

In einem rechts-nach-links-Elternelement startet die Füllung von rechts, und die Pfeiltasten folgen ihr. Übergib locale für die Ziffern des Lesers.

import { Slider, SliderLabel, SliderValue } from "@/components/ui/slider"

export function SliderRtl() {
  return (
    <div dir="rtl" className="flex w-full max-w-xs flex-col gap-8">
      <Slider defaultValue={65} locale="ar-EG">
        <SliderLabel>مستوى الصوت</SliderLabel>
        <SliderValue />
      </Slider>
      <Slider defaultValue={[20, 70]} locale="ar-EG" draggableRange>
        <SliderLabel>النطاق</SliderLabel>
        <SliderValue />
      </Slider>
    </div>
  )
}
TasteAktion
TabSetzt den Fokus auf den nächsten Thumb.
→↑Erhöht den Wert um einen Schritt. → verringert ihn in rechts-nach-links.
←↓Verringert den Wert um einen Schritt. ← erhöht ihn in rechts-nach-links.
Shift + ArrowPage UpPage DownBewegt um largeStep.
HomeSetzt das Minimum oder in einem Bereich den Wert des vorherigen Thumbs.
EndSetzt das Maximum oder in einem Bereich den Wert des nächsten Thumbs.
  • Jeder Thumb enthält ein natives input type="range" und ist damit ein slider mit eigenem Wert, min und max.
  • <SliderLabel /> benennt jeden Thumb. Ohne eines wird aria-label an <Slider /> an die Thumbs weitergegeben, nicht an den Wrapper, sodass der Slider nie unbenannt bleibt. Nutze in einem Bereich getAriaLabel, um die Thumbs zu unterscheiden.
  • <SliderValue /> und die Wertblase werden nicht bei jeder Änderung angesagt; der Thumb meldet seinen Wert bereits.
  • Das Drücken eines Thumbs zeigt keinen Fokusring; die Pfeiltasten tun es. Bei reduzierter Bewegung springt der Thumb, statt zu gleiten.
  • Auf Touchscreens wächst die Trefferfläche auf mindestens 44px, ohne das Layout zu ändern.

Gebaut auf dem Base UI Slider. Jeder Teil akzeptiert die Props des Primitivs, das er umschließt.

PropTypStandard
valueDer kontrollierte Wert. Ein Thumb pro Element.
number | number[]–
defaultValue
number | number[]–
onValueChangeFeuert bei jeder Änderung während des Ziehens.
(value, eventDetails) => void–
onValueCommittedFeuert einmal, wenn eine Änderung endet.
(value, eventDetails) => void–
min
number0
max
number100
step
number1
largeStep
number10
minStepsBetweenValues
number0
size
"sm" | "default" | "lg""default"
orientation
"horizontal" | "vertical""horizontal"
draggableRangeIn einem Bereich bewegt das Ziehen zwischen den Thumbs beide gemeinsam und hält den Abstand.
booleanfalse
showValueZeigt den Wert über dem Thumb, während gezogen oder per Tastatur fokussiert wird.
booleanfalse
thumbAlignmentedge hält den Thumb bei min und max innerhalb der Spur.
"edge" | "center" | "edge-client-only""edge"
thumbCollisionBehavior
"push" | "swap" | "none""push"
format
Intl.NumberFormatOptions–
locale
Intl.LocalesArgument"en-US"
aria-labelBenennt die Thumbs.
string–
getAriaLabelBenennt jeden Thumb eines Bereichs.
(index: number) => string–
getAriaValueText
(formattedValue: string, value: number, index: number) => string–
name
string–
disabled
booleanfalse
className
string | (state) => string–
render
ReactElement | (props, state) => ReactElement<div>
AttributBeschreibung
data-slot="slider"Das Root.
data-sizesm, default oder lg.
data-jumpVorhanden für 220ms, während der Thumb zu einem neuen Wert gleitet.
data-range-draggingVorhanden, solange der ganze Bereich gezogen wird.
data-orientationhorizontal oder vertical.
data-draggingVorhanden, solange ein Zeiger gedrückt ist.
data-disabledVorhanden, wenn deaktiviert.
data-invalidVorhanden, wenn in einem Field ungültig.
--slider-thumbDer Durchmesser des Thumbs, über size gesetzt.
--slider-trackDie Dicke der Spur, über size gesetzt.
data-slot="slider-control"Die drückbare Fläche um die Spur.
data-slot="slider-track"Die Spur.
data-slot="slider-range"Die Füllung zwischen dem Start oder dem ersten Thumb und dem letzten Thumb.
data-slot="slider-thumb"Jeder Thumb. data-index gibt seine Position an.
data-activeAm Thumb, der fokussiert ist oder gezogen wird.
data-slot="slider-thumb-value"Die Wertblase von showValue.

Benennt die Thumbs und wächst, um die Zeile zu füllen. Rendert ein <div>.

PropTypStandard
render
ReactElement | (props, state) => ReactElement<div>
AttributBeschreibung
data-slot="slider-label"Das Label.

Zeigt die formatierten Werte, in einem Bereich mit einem Halbgeviertstrich verbunden. Rendert ein <output>.

PropTypStandard
childrenEigener Text.
(formattedValues: string[], values: number[]) => ReactNode–
render
ReactElement | (props, state) => ReactElement<output>
AttributBeschreibung
data-slot="slider-value"Der Wert.
data-orientationhorizontal oder vertical.
data-draggingVorhanden, solange ein Zeiger gedrückt ist.
data-disabledVorhanden, wenn deaktiviert.
data-invalidVorhanden, wenn in einem Field ungültig.

In Blocks verwendet

Blocks, die auf Slider aufbauen.