import { Spinner } from "@/components/ui/spinner";
export default function SpinnerSizes() {
return (
<div className="flex flex-wrap items-center gap-3">
<Spinner className="size-3" />
<Spinner />
<Spinner className="size-5" />
<Spinner className="size-6" />
<Spinner className="size-8" />
</div>
);
}- Add the registryOnce per project
pnpm dlx shadcn@latest registry add @jarvis=https://ui.jarv.is/r/{name}.json - Add spinner
pnpm dlx shadcn@latest add @jarvis/spinner
Usage
import { Spinner } from "@/components/ui/spinner";
export function LoadingProjects() {
return (
<span className="flex items-center gap-2 text-sm text-muted-foreground">
<Spinner />
Loading projects…
</span>
);
}Use size-* and text-* classes to resize and recolor it. Pass an aria-label to say what's
loading.
Examples
Colors
import { Spinner } from "@/components/ui/spinner";
export default function SpinnerColors() {
return (
<div className="flex flex-wrap items-center gap-3">
<Spinner className="text-foreground" />
<Spinner className="text-brand" />
<Spinner className="text-success" />
<Spinner className="text-warning" />
<Spinner className="text-destructive" />
</div>
);
}In buttons
import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner";
export default function SpinnerInButtons() {
return (
<div className="flex flex-wrap items-center gap-3">
<Button variant="secondary" disabled>
<Spinner />
Loading
</Button>
<Button variant="outline" size="sm" disabled>
<Spinner />
Please wait
</Button>
<Button variant="secondary" size="icon" disabled aria-label="Loading">
<Spinner />
</Button>
</div>
);
}Loading button
Disable the button with focusableWhenDisabled while it works, so focus stays put, and point
aria-labelledby at its label so the changing text names it.
import * as React from "react";
import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner";
import { toast } from "@/components/ui/toast";
export default function SpinnerLoadingButton() {
const [saving, setSaving] = React.useState(false);
const timer = React.useRef<ReturnType<typeof setTimeout>>(undefined);
const labelId = React.useId();
React.useEffect(() => () => clearTimeout(timer.current), []);
function save() {
setSaving(true);
timer.current = setTimeout(() => {
setSaving(false);
toast.add({ title: "Changes saved", type: "success" });
}, 2000);
}
return (
<Button
variant="primary"
disabled={saving}
focusableWhenDisabled
aria-labelledby={labelId}
onClick={save}
>
{saving && <Spinner />}
<span id={labelId}>{saving ? "Saving…" : "Save changes"}</span>
</Button>
);
}In context
SyncingLoading projects…
import { Badge } from "@/components/ui/badge";
import { Spinner } from "@/components/ui/spinner";
export default function SpinnerInContext() {
return (
<div className="flex flex-wrap items-center gap-3">
<Badge variant="muted">
<Spinner className="size-3" />
Syncing
</Badge>
<span className="flex items-center gap-2 text-sm text-muted-foreground">
<Spinner />
Loading projects…
</span>
</div>
);
}Props
| Part | Extends |
|---|---|
| Spinner | <svg> |