Embossed UI

Search documentation

Search for a page or component.

Spinner

An indicator that can be used to show a loading state.

Processing payment...
$100.00
import { Item, ItemContent, ItemMedia, ItemTitle } from "@/components/ui/item"
import { Spinner } from "@/components/ui/spinner"

export function SpinnerDemo() {
  return (
    <div className="flex w-full max-w-xs flex-col gap-4">
      <Item variant="muted">
        <ItemMedia>
          <Spinner />
        </ItemMedia>
        <ItemContent>
          <ItemTitle className="line-clamp-1">Processing payment...</ItemTitle>
        </ItemContent>
        <ItemContent className="flex-none justify-end">
          <span className="text-sm text-muted-foreground tabular-nums">
            $100.00
          </span>
        </ItemContent>
      </Item>
    </div>
  )
}

Installation

pnpm dlx shadcn@latest add https://ui.ikramhasan.com/r/spinner.json

Usage

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

Eight spokes, each a step brighter, turn in eight ticks. Reduced motion halves the speed rather than stopping it, since the spinner is the only sign that work is happening.

Examples

Size

Use the size-* utilities to change the size of the spinner.

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

export function SpinnerSizes() {
  return (
    <div className="flex items-center gap-6">
      <Spinner className="size-3" />
      <Spinner className="size-4" />
      <Spinner className="size-6" />
      <Spinner className="size-8" />
    </div>
  )
}

Color

The spinner draws in currentColor. Use text-* utilities to change it.

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

export function SpinnerColor() {
  return (
    <div className="flex items-center gap-6">
      <Spinner className="size-6 text-muted-foreground" />
      <Spinner className="size-6 text-primary" />
      <Spinner className="size-6 text-success" />
      <Spinner className="size-6 text-destructive" />
    </div>
  )
}

Button

Add data-icon="inline-start" or data-icon="inline-end" to place a spinner in a button.

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

export function SpinnerButton() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-2">
      <Button disabled>
        <Spinner data-icon="inline-start" />
        Loading...
      </Button>
      <Button variant="secondary" disabled>
        <Spinner data-icon="inline-start" />
        Please wait
      </Button>
      <Button variant="outline" disabled>
        <Spinner data-icon="inline-start" />
        Processing
      </Button>
    </div>
  )
}

Badge

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

export function SpinnerBadge() {
  return (
    <div className="flex items-center gap-2">
      <Badge>
        <Spinner data-icon="inline-start" />
        Syncing
      </Badge>
      <Badge variant="secondary">
        <Spinner data-icon="inline-start" />
        Updating
      </Badge>
      <Badge variant="outline">
        <Spinner data-icon="inline-start" />
        Processing
      </Badge>
    </div>
  )
}

Input Group

import {
  InputGroup,
  InputGroupAddon,
  InputGroupInput,
} from "@/components/ui/input-group"
import { Spinner } from "@/components/ui/spinner"

export function SpinnerInputGroup() {
  return (
    <InputGroup className="max-w-xs">
      <InputGroupInput
        placeholder="Searching..."
        aria-label="Search"
        disabled
      />
      <InputGroupAddon align="inline-end">
        <Spinner />
      </InputGroupAddon>
    </InputGroup>
  )
}

API Reference

Spinner renders an svg with role="status" and aria-label="Loading", and accepts all of its props.