Github

Spinner

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

Spinner SVG
Processing payment...Please wait
$100.00
import { Spinner } from "@/components/ui/spinner";import { Surface } from "@/components/ui/surface";
export function SpinnerDemo() {	return (		<Surface className="flex w-full max-w-sm items-center gap-3 rounded-3xl p-6">			<Spinner />			<div className="flex min-w-0 flex-1 flex-col">				<span className="truncate font-medium text-base">					Processing payment...				</span>				<span className="text-muted-foreground text-sm">Please wait</span>			</div>			<span className="text-base tabular-nums">$100.00</span>		</Surface>	);}

Installation

pnpm dlx shadcn@latest add @herocn/spinner

Usage

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

Examples

Variants

Use the variant prop to match the spinner color to your status or intent.

Spinner SVGSpinner SVGSpinner SVGSpinner SVGSpinner SVG
import { Spinner } from "@/components/ui/spinner";
export function SpinnerVariants() {	return (		<div className="flex flex-wrap items-center gap-6">			<Spinner variant="default" />			<Spinner variant="primary" />			<Spinner variant="success" />			<Spinner variant="warning" />			<Spinner variant="destructive" />		</div>	);}

Size

Use the size prop from the HeroCN spinner (sm, default, lg, xl).

Spinner SVGSpinner SVGSpinner SVGSpinner SVG
import { Spinner } from "@/components/ui/spinner";
export function SpinnerSizes() {	return (		<div className="flex items-center gap-6">			<Spinner size="sm" />			<Spinner size="default" />			<Spinner size="lg" />			<Spinner size="xl" />		</div>	);}

Button

Add a spinner to a button to indicate a loading state. Place the spinner before the label with data-icon="inline-start" or after it with data-icon="inline-end".

import { Button } from "@/components/ui/button";import { Spinner } from "@/components/ui/spinner";
export function SpinnerButton() {	return (		<div className="flex flex-col items-center gap-4">			<Button disabled size="sm">				<Spinner data-icon="inline-start" size="sm" />				Loading...			</Button>			<Button variant="outline" disabled size="sm">				Please wait				<Spinner data-icon="inline-end" size="sm" />			</Button>			<Button variant="secondary" disabled size="sm">				<Spinner data-icon="inline-start" size="sm" />				Processing			</Button>		</div>	);}

Badge

Add a spinner to a badge to indicate background activity.

Spinner SVGSyncingUpdatingSpinner SVGSpinner SVGProcessing
import { Badge } from "@/components/ui/badge";import { Spinner } from "@/components/ui/spinner";
export function SpinnerBadge() {	return (		<div className="flex items-center gap-4">			<Badge>				<Spinner data-icon="inline-start" size="sm" />				Syncing			</Badge>			<Badge variant="primary">				Updating				<Spinner data-icon="inline-end" size="sm" />			</Badge>			<Badge variant="warning">				<Spinner data-icon="inline-start" size="sm" />				Processing			</Badge>		</div>	);}

Input Group

Use a spinner in input addons to indicate async validation or processing.

Spinner SVG
Spinner SVG Validating...
import { ArrowUpIcon } from "lucide-react";
import {	InputGroup,	InputGroupAddon,	InputGroupButton,	InputGroupInput,	InputGroupTextarea,} from "@/components/ui/input-group";import { Spinner } from "@/components/ui/spinner";
export function SpinnerInputGroup() {	return (		<div className="flex w-full max-w-md flex-col gap-4">			<InputGroup>				<InputGroupInput placeholder="Send a message..." disabled />				<InputGroupAddon align="inline-end">					<Spinner size="sm" />				</InputGroupAddon>			</InputGroup>			<InputGroup>				<InputGroupTextarea placeholder="Send a message..." disabled />				<InputGroupAddon align="block-end">					<Spinner size="sm" /> Validating...					<InputGroupButton disabled className="ml-auto" variant="default">						<ArrowUpIcon />						<span className="sr-only">Send</span>					</InputGroupButton>				</InputGroupAddon>			</InputGroup>		</div>	);}

Empty

Use a spinner in empty states when data is loading.

Spinner SVG
Processing your request
Please wait while we process your request. Do not refresh the page.
import { Button } from "@/components/ui/button";import {	Empty,	EmptyContent,	EmptyDescription,	EmptyHeader,	EmptyMedia,	EmptyTitle,} from "@/components/ui/empty";import { Spinner } from "@/components/ui/spinner";
export function SpinnerEmpty() {	return (		<Empty className="w-full">			<EmptyHeader>				<EmptyMedia variant="icon">					<Spinner />				</EmptyMedia>				<EmptyTitle>Processing your request</EmptyTitle>				<EmptyDescription>					Please wait while we process your request. Do not refresh the page.				</EmptyDescription>			</EmptyHeader>			<EmptyContent>				<Button variant="outline" size="sm">					Cancel				</Button>			</EmptyContent>		</Empty>	);}

RTL

Spinner SVG
جاري معالجة الدفع...
١٠٠.٠٠ دولار
"use client";
import {	type Translations,	useTranslation,} from "@/components/language-selector";import { Spinner } from "@/components/ui/spinner";import { Surface } from "@/components/ui/surface";
const translations: Translations = {	en: {		dir: "ltr",		values: {			title: "Processing payment...",			amount: "$100.00",		},	},	ar: {		dir: "rtl",		values: {			title: "جاري معالجة الدفع...",			amount: "١٠٠.٠٠ دولار",		},	},	he: {		dir: "rtl",		values: {			title: "מעבד תשלום...",			amount: "$100.00",		},	},};
export function SpinnerRtl() {	const { dir, language, t } = useTranslation(translations, "ar");
	return (		<Surface			className="flex w-full max-w-sm items-center gap-3 rounded-3xl p-6"			lang={language}			dir={dir}		>			<Spinner />			<div className="flex min-w-0 flex-1 flex-col">				<span className="truncate font-medium text-sm">{t.title}</span>			</div>			<span className="text-sm tabular-nums">{t.amount}</span>		</Surface>	);}

API Reference

Spinner

The Spinner component accepts all native SVG props plus the following:

PropTypeDefault