HextaUI

Spinner

Apple風の目盛りまたは呼吸するリングで表示する読み込みインジケーターです。表示までの待機と、ちらつかない最短表示時間を設定できます。

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

コンポーネント、HextaUIのテーマトークン、依存するHextaUIコンポーネントを追加します。

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

デフォルトは Apple のプラットフォームの8本スポークのインジケーターです。variant="ring" は円弧が伸び縮みしながら回るので、止まっているのではなく作業中に見えます。どちらも現在のテキストの色で描画されます。

ちらつきなし

loading を渡すと、スピナーは delay(150ms)待ってから表示されるため、速い読み込みでは一瞬表示されることがなく、表示されたら最低でも minDuration(400ms)は残ります。

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

サイズ

どちらのバリアントにも sm、default、lg、xl。

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

インライン

テキストの隣では、言葉が伝えるようにスピナーを aria-hidden にします。単独では 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>
  )
}

ボタン内

Button と Command は、読み込み状態にこのスピナーを使い、ボタンのアイコンに合わせたサイズにします。

"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>
  )
}
  • 単独では、スピナーは label(「Loading」)で名前が付いた status です。
  • aria-hidden を指定すると role が外れます。表示されているテキストの隣や、処理中のボタンの内側で使います。
  • 視差効果の軽減が有効な場合、回転せずにゆっくり脈動するため、何かが進行中であることは伝わります。
プロパティ型デフォルト
variant
"ticks" | "ring""ticks"
sizenull ではサイズを親に任せます。
"sm" | "default" | "lg" | "xl" | null"default"
label
string"Loading"
loading遅延表示を有効にします。常に表示するには省略します。
boolean–
delay
number150
minDuration
number400
animated非表示にせずにアニメーションを一時停止します。
booleantrue
属性説明
data-slot="spinner"SVG。data-variant を持ちます。
const visible = useDelayedLoading(isFetching, { delay: 150, minDuration: 400 })

スケルトン、オーバーレイ、一瞬でも表示すべきでないあらゆるものに使える、同じタイミングのフックです。useDelayedLoading ガイドを参照してください。

使用しているブロック

Spinner の上に構築されるブロック。