Spinner
Server-safeAn indeterminate loading indicator.
Live preview
Rendered from the installed VEXA source — the same files you receive.
LoadingLoadingLoading
Installation
Adds the component source to your project. Registry dependencies are pulled in automatically.
npx shadcn@latest add https://vexa.valfiguer.com/r/spinner.jsonInstalls to components/vexa/spinner.tsx
npm dependencies
No external npm dependencies.
Registry dependencies
- @vexa/utils
Other VEXA items this component needs. Install them first — they are resolved automatically by the CLI.
Source
Copy this into its target path, or install it with the command above.
components/vexa/spinner.tsx
import type * as React from "react";
import { cn } from "@/lib/utils";
export interface SpinnerProps extends React.ComponentProps<"svg"> {
/**
* Accessible loading text announced via a visually-hidden `status` region.
* Provide a translated string; defaults to `"Loading"`.
*/
label?: string;
}
/**
* Spinner — an indeterminate loading indicator.
*
* RSC-safe. Exposes a `role="status"` live region with a visually-hidden label
* so screen readers announce the loading state. The rotation is disabled under
* reduced-motion while the accessible status text remains.
*/
export function Spinner({
className,
label = "Loading",
...props
}: SpinnerProps) {
return (
<span role="status" className="inline-flex items-center">
<svg
data-slot="spinner"
viewBox="0 0 24 24"
fill="none"
aria-hidden="true"
className={cn(
"size-4 animate-spin text-current motion-reduce:animate-none",
className,
)}
{...props}
>
<circle
cx="12"
cy="12"
r="9"
stroke="currentColor"
strokeOpacity="0.25"
strokeWidth="2.5"
/>
<path
d="M21 12a9 9 0 0 0-9-9"
stroke="currentColor"
strokeWidth="2.5"
strokeLinecap="round"
/>
</svg>
<span className="sr-only">{label}</span>
</span>
);
}
Details
Example
tsx
<Spinner label="Saving" className="text-vexa-focus" />Accessibility
role=status live region with a visually-hidden label; rotation disabled under reduced motion.