Skip to content
VEXA
Feedback

Spinner

Server-safe

An 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.json

Installs 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.