Shadcn Spinner
Free shadcn/ui spinner component for React — a Loader2 + animate-spin loading indicator. Size it, color it, drop it in a button, or center it as a loading state, plus bonus ring/dots/bars CSS styles.
Installation
{
"registries": {
"@designrevision": "https://registry.designrevision.com/r/{name}.json"
}
}
Add to your existing components.json. Requires shadcn/ui v2.3+.
Packages
Props
No props documented yet.
import { Loader2Icon } from "lucide-react"
import { cn } from "@/lib/utils"
function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
return (
<Loader2Icon
role="status"
aria-label="Loading"
className={cn("size-4 animate-spin", className)}
{...props}
/>
)
}
export { Spinner }
Examples
Sizes
The spinner at four sizes — controlled with a single `size-*` utility.
Packages
Props
No props documented yet.
import { Spinner } from "@/components/ui/spinner"
export default function SpinnerDemo01() {
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>
)
}
Loading button
The #1 use — a Spinner inside a disabled Button for a pending action.
Packages
Props
No props documented yet.
import { Button } from "@/components/ui/button"
import { Spinner } from "@/components/ui/spinner"
export default function SpinnerButton01() {
return (
<div className="flex flex-wrap items-center gap-4">
<Button disabled>
<Spinner />
Please wait
</Button>
<Button variant="outline" disabled>
<Spinner />
Saving
</Button>
<Button size="icon" disabled>
<Spinner />
<span className="sr-only">Loading</span>
</Button>
</div>
)
}
Colors
The spinner uses `currentColor` — tint it with any `text-*` utility.
Packages
Props
No props documented yet.
import { Spinner } from "@/components/ui/spinner"
export default function SpinnerColors01() {
return (
<div className="flex items-center gap-6">
<Spinner className="size-6" />
<Spinner className="size-6 text-primary" />
<Spinner className="size-6 text-destructive" />
<Spinner className="size-6 text-muted-foreground" />
<Spinner className="size-6 text-green-600" />
</div>
)
}
Loading state
A centered spinner with a label — a section or card loading state.
Packages
Props
No props documented yet.
import { Spinner } from "@/components/ui/spinner"
export default function SpinnerCard01() {
return (
<div className="flex h-48 w-full max-w-sm items-center justify-center rounded-lg border">
<div className="flex flex-col items-center gap-3">
<Spinner className="size-8 text-muted-foreground" />
<p className="text-muted-foreground text-sm">Loading…</p>
</div>
</div>
)
}
Inline with text
A spinner inline with a status message — "Checking…" / "Uploading…".
Packages
Props
No props documented yet.
import { Spinner } from "@/components/ui/spinner"
export default function SpinnerInline01() {
return (
<div className="flex flex-col gap-3 text-sm">
<span className="flex items-center gap-2">
<Spinner className="size-4" />
Checking availability…
</span>
<span className="text-muted-foreground flex items-center gap-2">
<Spinner className="size-4" />
Uploading files…
</span>
</div>
)
}
Spinner styles (ring / dots / bars)
Bonus spinner styles built with plain CSS — beyond the canonical Loader2.
Packages
Props
No props documented yet.
// Bonus spinner styles built with plain CSS — beyond the canonical Loader2 Spinner.
// The "bars" variant uses the .animate-spinner-bar keyframe from globals.css.
export default function SpinnerStyles01() {
return (
<div className="flex items-center gap-10">
<div className="flex flex-col items-center gap-2">
<div className="size-8 animate-spin rounded-full border-2 border-current border-t-transparent text-primary" />
<span className="text-muted-foreground text-xs">Ring</span>
</div>
<div className="flex flex-col items-center gap-2">
<div className="flex items-center gap-1">
<div className="size-2 animate-bounce rounded-full bg-primary [animation-delay:-0.3s]" />
<div className="size-2 animate-bounce rounded-full bg-primary [animation-delay:-0.15s]" />
<div className="size-2 animate-bounce rounded-full bg-primary" />
</div>
<span className="text-muted-foreground text-xs">Dots</span>
</div>
<div className="flex flex-col items-center gap-2">
<div className="flex h-8 items-end gap-1">
<div className="animate-spinner-bar h-full w-1.5 origin-bottom rounded-full bg-primary [animation-delay:-0.4s]" />
<div className="animate-spinner-bar h-full w-1.5 origin-bottom rounded-full bg-primary [animation-delay:-0.2s]" />
<div className="animate-spinner-bar h-full w-1.5 origin-bottom rounded-full bg-primary" />
</div>
<span className="text-muted-foreground text-xs">Bars</span>
</div>
</div>
)
}
The Shadcn Spinner is the simplest loading indicator: Lucide's Loader2 icon with animate-spin and the right accessibility attributes. Because it is just an SVG, you size it with size-* and color it with text-* — and it slots straight into a button or a centered loading state. The examples below cover sizing, color, loading buttons, overlays, and a few bonus spinner styles.
It's a Loader2 icon
The whole component is a wrapper:
import { Loader2Icon } from "lucide-react"
import { cn } from "@/lib/utils"
function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
return (
<Loader2Icon
role="status"
aria-label="Loading"
className={cn("size-4 animate-spin", className)}
{...props}
/>
)
}
The only dependency is lucide-react. Swap Loader2Icon for LoaderIcon or LoaderCircleIcon if you prefer a different look.
Size and color
- Size with
size-*:<Spinner className="size-3" />for inline,size-8for a page loader. Default issize-4. - Color with
text-*: the icon usescurrentColor, so<Spinner className="text-primary" />(ortext-destructive,text-muted-foreground) just works — and inside a button it inherits the button's color.
The loading button
The most common use is a pending button — put the spinner before the label and disable it:
<Button disabled>
<Spinner />
Please wait
</Button>
The Button already spaces and sizes SVG children, so no extra styling is needed.
A centered loading state
For a section or card that is still fetching, center the spinner:
<div className="flex h-48 items-center justify-center">
<Spinner className="size-8 text-muted-foreground" />
</div>
This is also the ideal Suspense fallback or "while loading" branch.
Bonus: ring, dots & bars
The canonical spinner is the Loader2 icon, but the Spinner styles example shows three more, all in plain CSS with your tokens:
- Ring — a bordered circle with one transparent edge and
animate-spin. - Dots — three dots with
animate-bounceand staggeredanimation-delay. - Bars — scaling bars driven by a small keyframe.
Accessibility
The Spinner carries role="status" and aria-label="Loading", so screen readers announce the loading state. Keep that for standalone loaders; when the spinner is inside a button or label that already names the action, the surrounding context describes it.