import { Ring } from "@/components/loading-ui/ring";
export function RingDemo() {pnpm dlx shadcn add @loading-ui/ring
import { cn } from "@/lib/utils";
function Ring({ className, ...props }: React.ComponentProps<"svg">) {
return (
<svg
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={cn("animate-spin", className)}
{...props}
>
<path
d="M21 12.0004C20.9999 13.901 20.3981 15.7528 19.2809 17.2904C18.1637 18.8279 16.5885 19.9723 14.7809 20.5596C12.9733 21.1469 11.0262 21.1468 9.21864 20.5594C7.41109 19.9721 5.83588 18.8276 4.71876 17.29C3.60165 15.7523 2.99999 13.9005 3 11.9999C3.00001 10.0993 3.60171 8.24755 4.71884 6.70994C5.83598 5.17233 7.4112 4.02785 9.21877 3.44052C11.0263 2.85319 12.9734 2.85316 14.781 3.44044"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
export { Ring };import { Ring } from "@/components/ring";<Ring className="size-4" />Use Ring when you want a loader that reads clearly at a glance and fits naturally into buttons, cards, dialogs, and route-level pending UI. It works well when the user only needs confirmation that work is in progress, not a precise percentage.
Use Ring next to a label when an action is pending.
import { Ring } from "@/components/ring";
export function SaveButton({ isSaving }: { isSaving: boolean }) {
return (
<button
type="button"
disabled={isSaving}
className="inline-flex items-center gap-2 rounded-md border px-3 py-2"
>
{isSaving ? <Ring className="size-4" /> : null}
<span>{isSaving ? "Saving..." : "Save changes"}</span>
</button>
);
}Use it as a simple fallback for route or section loading.
import { Ring } from "@/components/ring";
export function LoadingSection() {
return (
<div className="flex items-center justify-center py-12">
<Ring className="size-6 text-muted-foreground" />
</div>
);
}Ring inherits currentColor, so it responds well to utility classes.
<Ring className="size-4 text-muted-foreground" />
<Ring className="size-6 text-foreground" />
<Ring className="size-8 text-primary" />On its own, Ring is purely visual. When the state is not already obvious from nearby text, pair it with accessible copy such as Saving..., Loading results..., or screen-reader-only status text.
The component is just an animated SVG path, so customization is straightforward:
animate-spin