HextaUI

Spinner

Um indicador de carregamento com tracinhos no estilo Apple ou um anel que "respira", que pode esperar antes de aparecer e permanecer o suficiente para não piscar.

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

Adiciona o componente, os tokens de tema do HextaUI e quaisquer componentes do HextaUI dos quais ele depende.

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

O padrão é o indicador de oito raios das plataformas Apple. variant="ring" gira enquanto seu arco cresce e encolhe, então parece trabalhando em vez de travado. Ambos são desenhados na cor atual do texto.

Sem piscar

Passe loading e o spinner espera delay (150ms) antes de aparecer, para carregamentos rápidos nunca o exibirem, e depois permanece por pelo menos minDuration (400ms) quando fica visível.

"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>
  )
}

Tamanhos

sm, default, lg e xl para as duas variantes.

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

Ao lado de texto, marque o spinner com aria-hidden para que as palavras falem por ele. Sozinho, ele anuncia seu label.

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>
  )
}

Em botões

Button e Command usam este spinner nos estados de carregamento, dimensionado para os ícones do botão.

"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>
  )
}
  • Sozinho, o spinner é um status nomeado por label (“Loading”).
  • Com aria-hidden, perde seu role, para uso ao lado de texto visível ou dentro de um botão ocupado.
  • Com movimento reduzido, pulsa suavemente em vez de girar, para ainda mostrar que algo está acontecendo.
PropTipoPadrão
variant
"ticks" | "ring""ticks"
sizenull deixa o dimensionamento para o pai.
"sm" | "default" | "lg" | "xl" | null"default"
label
string"Loading"
loadingAtiva a exibição com atraso. Omita para sempre mostrar.
boolean–
delay
number150
minDuration
number400
animatedPausa a animação sem ocultá-la.
booleantrue
AtributoDescrição
data-slot="spinner"O SVG, com data-variant.
const visible = useDelayedLoading(isFetching, { delay: 150, minDuration: 400 })

O mesmo tempo como um hook, para skeletons, overlays ou qualquer coisa que não deva piscar. Veja o guia do useDelayedLoading.

Usado em blocos

Blocos que se baseiam em Spinner.