Spinner

A compact status mark for work in progress.

A small status mark for pending work. Use it on its own, in a card, or inside a button or badge.

Processing payment…
$100.00
tsx
import { Card, CardContent } from "@/components/ui/card"import { Spinner } from "@/components/ui/spinner"export function Example() {  return (    <Card className="w-full max-w-sm border-0 py-0">      <CardContent className="flex items-center justify-between gap-4 py-3">        <div className="flex items-center gap-2.5">          <Spinner />          <span className="text-sm tracking-wide">Processing payment…</span>        </div>        <span className="text-sm tracking-wide text-muted-foreground">          $100.00        </span>      </CardContent>    </Card>  )}

Installation

npx @arctis-sh/@arctis-sh/ui@latest add spinner

Usage

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

Customization

Replace the SVG inside Spinner with your own mark. Keep role="status", an aria-label, and animate-spin.

tsx
import { cn } from "@/lib/utils"function Spinner({ className, ...props }: React.ComponentProps<"svg">) {  return (    <svg      viewBox="0 0 24 24"      fill="none"      role="status"      aria-label="Loading"      data-slot="spinner"      className={cn("size-4 animate-spin", className)}      {...props}    >      <g stroke="currentColor" strokeWidth="2" strokeLinecap="round">        <line x1="12" y1="2" x2="12" y2="6" opacity="1" />        <line x1="12" y1="2" x2="12" y2="6" opacity="0.875" transform="rotate(45 12 12)" />        <line x1="12" y1="2" x2="12" y2="6" opacity="0.75" transform="rotate(90 12 12)" />        <line x1="12" y1="2" x2="12" y2="6" opacity="0.625" transform="rotate(135 12 12)" />        <line x1="12" y1="2" x2="12" y2="6" opacity="0.5" transform="rotate(180 12 12)" />        <line x1="12" y1="2" x2="12" y2="6" opacity="0.375" transform="rotate(225 12 12)" />        <line x1="12" y1="2" x2="12" y2="6" opacity="0.25" transform="rotate(270 12 12)" />        <line x1="12" y1="2" x2="12" y2="6" opacity="0.125" transform="rotate(315 12 12)" />      </g>    </svg>  )}export function Example() {  return <Spinner />}

Size

Size it with utility classes like size-3, size-4, size-6, or size-8.

tsx
import { Spinner } from "@/components/ui/spinner"export function Example() {  return (    <>      <Spinner className="size-3" />      <Spinner className="size-4" />      <Spinner className="size-6" />      <Spinner className="size-8" />    </>  )}

Button

Use Button’s loading prop, or drop in a Spinner with data-icon="inline-start" or data-icon="inline-end".

tsx
import { Button } from "@/components/ui/button"import { Spinner } from "@/components/ui/spinner"export function Example() {  return (    <>      <Button loading>Saving</Button>      <Button disabled>        <Spinner data-icon="inline-start" aria-hidden />        Loading…      </Button>      <Button variant="outline" disabled>        <Spinner data-icon="inline-start" aria-hidden />        Please wait      </Button>      <Button variant="secondary" disabled>        Processing        <Spinner data-icon="inline-end" aria-hidden />      </Button>    </>  )}

Badge

Use the same pattern in a Badge for a compact in-progress status.

SyncingUpdatingProcessing
tsx
import { Badge } from "@/components/ui/badge"import { Spinner } from "@/components/ui/spinner"export function Example() {  return (    <>      <Badge variant="secondary">        <Spinner data-icon="inline-start" aria-hidden />        Syncing      </Badge>      <Badge variant="outline">        <Spinner data-icon="inline-start" aria-hidden />        Updating      </Badge>      <Badge>        Processing        <Spinner data-icon="inline-end" aria-hidden />      </Badge>    </>  )}

API Reference

Spinner

PropTypeDefault
classNamestring

Also accepts the other native svg attributes.

<Spinner />