ComponentsCSS
Spinner
Loading indicator in the current text colour. Built into Button as its loading state.
Installation
npx shadcn@latest add https://ui-collections.vercel.app/r/tide/spinner.jsonAlso installs the theme, cn(). Packages: clsx, tailwind-merge.
Usage
spinner-demo.tsx
"use client";
import { useState } from "react";
import { Button } from "@/registry/kit/ui/button";
import { Spinner } from "@/registry/kit/ui/spinner";
export default function SpinnerDemo() {
const [saving, setSaving] = useState(false);
function save() {
setSaving(true);
setTimeout(() => setSaving(false), 1800);
}
return (
<div className="flex flex-col items-center gap-10">
<div className="flex items-center gap-6 text-muted">
<Spinner size="sm" />
<Spinner />
<Spinner size="lg" className="text-accent" />
</div>
<div className="flex flex-wrap items-center justify-center gap-3">
<Button variant="accent" loading={saving} onClick={save}>
{saving ? "Saving…" : "Save changes"}
</Button>
<Button loading>Deploying</Button>
</div>
</div>
);
}API
size= "md"
"sm" | "md" | "lg"Diameter.
label= "Loading"
string | nullAnnounced as a status. Pass null when a parent already reports busy.
Source
components/ui/spinner.tsx
import { cn } from "@/registry/kit/lib/utils";
const sizes = { sm: "size-3.5", md: "size-5", lg: "size-8" };
export interface SpinnerProps {
size?: keyof typeof sizes;
/** Announced to screen readers. Pass `null` when a parent already says it's busy. */
label?: string | null;
className?: string;
}
/**
* Loading indicator in the current text colour: a faint track and a turning
* arc. Keeps spinning under reduced motion, since a frozen spinner reads as
* finished.
*/
export function Spinner({ size = "md", label = "Loading", className }: SpinnerProps) {
return (
<span
role={label ? "status" : undefined}
aria-label={label ?? undefined}
aria-hidden={label ? undefined : true}
className={cn("inline-block shrink-0", sizes[size], className)}
>
<svg viewBox="0 0 24 24" fill="none" className="size-full animate-spin" aria-hidden>
<circle cx="12" cy="12" r="9.5" stroke="currentColor" strokeOpacity="0.2" strokeWidth="2.5" />
<path d="M21.5 12A9.5 9.5 0 0 0 12 2.5" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" />
</svg>
</span>
);
}