HextaUI

Spinner

Ein Ladeindikator mit Ticks im Apple-Stil oder einem atmenden Ring, der vor dem Anzeigen warten und lange genug sichtbar bleiben kann, um nicht zu flackern.

import { Spinner } from "@/components/ui/spinner"

export function SpinnerDemo() {
  return (
    <div className="flex items-center gap-10">
      <Spinner size="xl" />
      <Spinner size="xl" variant="ring" />
    </div>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/spinner.json

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

import { Spinner } from "@/components/ui/spinner"
<Spinner />
<Spinner variant="ring" size="lg" />
<Spinner loading={isFetching} />

Der Standard ist der Acht-Speichen-Indikator von Apple-Plattformen. variant="ring" dreht sich, während sein Bogen wächst und schrumpft, sodass er nach Arbeit statt nach Hängen aussieht. Beide werden in der aktuellen Textfarbe gezeichnet.

Ohne Flackern

Übergib loading, und der Spinner wartet delay (150ms), bevor er erscheint, sodass schnelle Ladevorgänge ihn nie aufblitzen lassen, und bleibt dann mindestens minDuration (400ms) sichtbar.

"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import { Spinner } from "@/components/ui/spinner"

export function SpinnerDelayed() {
  const [loading, setLoading] = React.useState(false)

  const load = (ms: number) => {
    setLoading(true)
    setTimeout(() => setLoading(false), ms)
  }

  return (
    <div className="flex max-w-full min-w-0 flex-col items-center gap-4">
      <div className="flex size-8 items-center justify-center">
        <Spinner loading={loading} size="lg" />
      </div>
      <div className="flex flex-wrap justify-center gap-2">
        <Button variant="outline" size="sm" onClick={() => load(100)}>
          Fast load (100ms)
        </Button>
        <Button variant="outline" size="sm" onClick={() => load(250)}>
          Load (250ms)
        </Button>
        <Button variant="outline" size="sm" onClick={() => load(2000)}>
          Slow load (2s)
        </Button>
      </div>
    </div>
  )
}

Größen

sm, default, lg und xl für beide Varianten.

import { Spinner } from "@/components/ui/spinner"

const sizes = ["sm", "default", "lg", "xl"] as const

export function SpinnerSizes() {
  return (
    <div className="flex flex-col gap-6">
      <div className="flex items-center gap-6">
        {sizes.map((size) => (
          <Spinner key={size} size={size} />
        ))}
      </div>
      <div className="flex items-center gap-6">
        {sizes.map((size) => (
          <Spinner key={size} size={size} variant="ring" />
        ))}
      </div>
    </div>
  )
}

Inline

Neben Text markiere den Spinner mit aria-hidden, damit die Worte sprechen. Allein sagt er sein label an.

import { Badge } from "@/components/ui/badge"
import { Spinner } from "@/components/ui/spinner"

export function SpinnerInline() {
  return (
    <div className="flex flex-col items-start gap-4 text-sm">
      <p className="flex items-center gap-2 text-muted-foreground">
        <Spinner size="sm" aria-hidden />
        Saving changes…
      </p>
      <Badge>
        <Spinner variant="ring" aria-hidden />
        Deploying
      </Badge>
      <p className="flex items-center gap-2 text-primary">
        <Spinner size="sm" label="Syncing" />
        Spinners use the text color around them.
      </p>
    </div>
  )
}

In Buttons

Button und Command nutzen diesen Spinner für ihre Ladezustände, in der Größe der Icons des Buttons.

"use client"

import { IconRefresh } from "@tabler/icons-react"

import { Button } from "@/components/ui/button"

function save() {
  return new Promise((resolve) => setTimeout(resolve, 1600))
}

export function SpinnerButton() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      <Button feedback loadingLabel="Saving…" onClick={save}>
        Save changes
      </Button>
      <Button
        variant="outline"
        size="icon"
        aria-label="Refresh"
        feedback
        onClick={save}
      >
        <IconRefresh />
      </Button>
    </div>
  )
}
  • Allein ist der Spinner ein status, benannt durch label („Loading“).
  • Mit aria-hidden verliert er seine Rolle, zur Verwendung neben sichtbarem Text oder in einem beschäftigten Button.
  • Bei reduzierter Bewegung pulsiert er sanft, statt sich zu drehen, und zeigt so weiterhin, dass etwas passiert.
PropTypStandard
variant
"ticks" | "ring""ticks"
sizenull überlässt die Größe dem Elternelement.
"sm" | "default" | "lg" | "xl" | null"default"
label
string"Loading"
loadingSchaltet verzögertes Anzeigen ein. Lass es weg, um immer anzuzeigen.
boolean–
delay
number150
minDuration
number400
animatedPausiere die Animation, ohne sie auszublenden.
booleantrue
AttributBeschreibung
data-slot="spinner"Das SVG, mit data-variant.
const visible = useDelayedLoading(isFetching, { delay: 150, minDuration: 400 })

Dasselbe Timing als Hook, für Skeletons, Overlays oder alles, was nicht aufblitzen soll. Siehe den useDelayedLoading-Guide.

In Blocks verwendet

Blocks, die auf Spinner aufbauen.