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>
);
}
pnpm dlx shadcn@latest add https://herocn.dev/r/spinner.jsonimport { Spinner } from "@/components/ui/spinner"<Spinner />Use the variant prop to match the spinner color to your status or intent.
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>
);
}
Use the size prop from the HeroCN spinner (sm, default, lg, xl).
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>
);
}
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>
);
}
Add a spinner to a badge to indicate background activity.
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>
);
}
Use a spinner in input addons to indicate async validation or processing.
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>
);
}
Use a spinner in empty states when data is loading.
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>
);
}
"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>
);
}
The Spinner component accepts all native SVG props plus the following:
| Prop | Type | Default |
|---|---|---|
| variant | "default" | "primary" | "destructive" | "success" | "warning" | "default" |
| size | "sm" | "default" | "lg" | "xl" | "default" |
| className | string | — |