Siteplane UI

Spinner

Indicate that an operation is still in progress.

Markdown
"use client";

import { Spinner } from "@/siteplane/components/spinner";

export default function Example() {
  return <Spinner />;
}

Installation

pnpm dlx shadcn@4.18.0 add @siteplane/spinner

Complete the one-time setup first. Registry JSON lists source files and dependencies.

For this page’s examples, also install:

pnpm dlx shadcn@4.18.0 add @siteplane/input-group

Usage

import { Spinner } from "@/siteplane/components/spinner";

<Spinner />;

Variants

Select an indeterminate visual treatment; none of these variants reports numeric progress.

default
ring
dots
bars
spokes
pulse
comet
"use client";

import { Spinner } from "@/siteplane/components/spinner";

export default function Example() {
  return (
    <div className="flex flex-wrap items-center gap-6">
      {(
        ["default", "ring", "dots", "bars", "spokes", "pulse", "comet"] as const
      ).map((variant) => (
        <div key={variant} className="grid justify-items-center gap-2">
          <Spinner variant={variant} />
          <span className="text-sm">{variant}</span>
        </div>
      ))}
    </div>
  );
}

Sizes

Without a size class, the default, comet, ring, pulse and spokes variants are 16 px. Scale a spinner with size utilities: those variants follow the root size, dots keep their mark size and bars scale only in height.

"use client";

import { Spinner } from "@/siteplane/components/spinner";

export default function Example() {
  return (
    <div className="flex items-center gap-6">
      <Spinner className="size-3" />
      <Spinner className="size-4" />
      <Spinner className="size-6" />
      <Spinner className="size-8" />
    </div>
  );
}

Labels

Name what is loading. When visible text already describes the operation, hide the spinner from assistive technology. For a pending button, use the Button loading prop or LoadingButton instead of placing a spinner in the button.

Loading projects…
"use client";

import { Spinner } from "@/siteplane/components/spinner";

export default function Example() {
  return (
    <div className="flex flex-wrap items-center gap-6">
      <div
        className="flex items-center gap-2 text-muted-foreground text-sm"
        role="status"
      >
        <Spinner aria-hidden="true" />
        Loading projects…
      </div>
      <Spinner aria-label="Loading comments" />
    </div>
  );
}

Input group

Show a spinner in an input group addon while a search or validation runs.

"use client";

import {
  InputGroup,
  InputGroupAddon,
  InputGroupInput,
} from "@/siteplane/components/input-group";
import { Spinner } from "@/siteplane/components/spinner";

export default function Example() {
  return (
    <InputGroup className="max-w-sm">
      <InputGroupInput
        aria-label="Search projects"
        defaultValue="Onboarding"
        type="search"
      />
      <InputGroupAddon align="inline-end">
        <Spinner aria-label="Searching" />
      </InputGroupAddon>
    </InputGroup>
  );
}

API reference

Component properties
PropTypeDefaultDescription
variantSpinnerVariantdefaultdefault, ring, dots, bars, spokes, pulse or comet.
classNamestringunsetAdditional classes; size utilities customize SVG, ring, pulse and spokes dimensions.
aria-labelstringLoadingAccessible loading label.
roleARIA rolestatusStatus semantics unless overridden.

SpinnerProps and SpinnerVariant are exported. The props and the ref follow the element that the variant renders, an SVG for default and comet and a div for the others. Dots size their marks directly, and bars keep a fixed bar width while their height follows the root.

Read the installed source.

Usage notes

Use Progress for measurable completion. A spinner does not start, cancel or finish a request. Your application owns pending/error state and when to remove it.