A small status mark for pending work. Use it on its own, in a card, or inside a button or badge.
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 spinnerUsage
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.
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
| Prop | Type | Default |
|---|---|---|
| className | string | — |
Also accepts the other native svg attributes.
<Spinner />