Skip to main content

What it is

A Spinner is a small circular mark that animates continuously to show that something is working. It carries no information beyond “in progress” — no percentage, no shape of what’s coming, nothing about how much longer. It has one form: indeterminate. There is a determinate mode in the underlying type interface, but nothing in this codebase demonstrates or themes it — see Variants, sizes, and states. That single-purpose narrowness is what separates it from its two neighbors in this category. A ProgressBar can show a real value; a Spinner never does. A Skeleton previews the shape of content that hasn’t arrived; a Spinner previews nothing about shape at all — it is for a wait too small, too compact, or too shapeless for either of those to fit.

Live example

No live example is available. This page’s real export, CircularProgress, has no Storybook story anywhere in this checkout — confirmed against both the source tree and the live Storybook index. See the warning below and Open issues.

Exports

Verified directly from CircularProgress.overrides.ts and the package’s top-level export list — no per-component prop emitter exists yet for this concept, so this table is hand-confirmed rather than generated.
The package exports this component as CircularProgress, re-exported directly from the underlying framework with no Titan wrapper file of its own — there is a CircularProgress.overrides.ts that themes it, but no CircularProgress.tsx. Import CircularProgress for the component this page documents; there is no export literally named Spinner. This is the same pattern TITAN-DIV-10 records for Table/DataGrid and others, and it already lists this exact mapping — Spinner is CircularProgress.

Choose Spinner when

  • Something is working, its duration is unknown, and there is no shape of incoming content to preview.
  • The space available is small or inline — a button, a compact region, a line of text — where a full-width ProgressBar would not fit.

Choose something else when

This codebase’s real full-page loading indicator is not this component. A separate, unrelated component called Loader (a five-bar bounce animation, exported alongside LoaderOverlay) is used dozens of times across many applications, per its own generated adoption data — while CircularProgress, the export this page documents, has exactly one recorded usage anywhere. Loader/LoaderOverlay have no page in this documentation site. Readers looking for “the spinner everyone actually uses” are very likely looking for Loader, not CircularProgress — see Open issues.

Anatomy

One element: a spinning ring. There is no label slot, no icon, and no container — a caption or surrounding message, if one is needed, is separate content placed beside it.

Variants, sizes, and states

The default color renders green, not the brand blue. CircularProgress.overrides.ts maps colorPrimary to theme.vars.palette.success.main. A Spinner with no color prop set — which is the common case, since primary is the default — spins in the same green used for success states, not a neutral color. The same choice appears in ProgressBar’s override for its own primary color, and in the Loader component’s bar color — a consistent house habit across this codebase’s loading primitives, not something decided for this component specifically.
One variant demonstrated: indeterminate. determinate exists in the type interface and is un-themed and un-storied. One themed color: primary. The other five accepted color values were not confirmed to route through any Titan-named token — CircularProgress.overrides.ts themes only colorPrimary. No size scale. size is a raw pixel number or CSS-unit string with no Titan-decided small/medium/large tier — whatever number is passed renders at that size.

Edge and failure states

No Titan test file exists for this component (there is no .test.tsx in its source directory), and no story exercises it — so nothing here is confirmed against this codebase’s own tests. The table below states what the underlying framework’s own type contract and source code say, not what Titan has verified.

Tokens

Verified directly from CircularProgress.overrides.ts — no per-component token emitter exists yet for this concept, so this table is hand-confirmed rather than generated.
The default color resolves through the underlying palette, not the tokens.* semantic layer most of this system’s other color decisions go through. It is the one line in this override file that reaches theme.vars.palette directly instead of theme.vars.tokens.

Composition

No Titan component in this codebase composes CircularProgress internally — it was not found inside any other component’s source during this pass. It is not used inside Button’s loading state, LoaderOverlay, or any other component checked.

Content

A Spinner carries no text. If a wait needs an explanation, put it beside the spinner as separate content — never inside it, and never a “Loading…” label overlapping the mark itself.

Accessibility

Renders role="progressbar" unconditionally, confirmed directly in the underlying component’s source — the same as LinearProgress. In indeterminate mode (the only mode this codebase demonstrates), no aria-valuenow/min/max are set, which is correct for a value that doesn’t exist. No accessible name is set by the component itself. The underlying framework’s own documentation for this component states that a spinner describing a specific region’s loading state should be paired with aria-describedby pointing at it, and the region itself should carry aria-busy. Neither is wired up automatically by anything in this codebase — it is a pattern to apply at the call site, not something the component provides.

Constraints

Known issues

Spinner: open issues

Divergences, open decisions, and undocumented gaps for Spinner.

Why it works this way

A Spinner says less than a ProgressBar on purpose. The moment a process can report real progress, a bar communicates more — which is why nothing in this codebase pairs a determinate value with the circular form. The circular mark is reserved for exactly the case where there is nothing more specific to say than “working.” Its size taking a raw number rather than a themed scale is a real gap, not a deliberate minimalism. Compare ProgressBar, which has no size prop either but is at least pinned to one uniform track-height token — a Spinner has neither a scale nor a single decided size, so every call site picks its own number.

Status

No lifecycle metadata exists. There is no status, since, deprecated, or replacedBy field on a Titan component, so this table cannot report when an export arrived or whether it is on the way out.
The generated adoption report for this component shows exactly one usage, in a single application. Unlike the Toast and ProgressBar figures elsewhere in this category, this count is not suspected of a tag-matching problem — the real import name (CircularProgress) matches what the report looked for. Genuinely low adoption, not an artifact of the measurement.
  • ProgressBar · Skeleton — the other two loading primitives, split by whether a value or a shape is known
  • Loader — a separate, unrelated component in this same package that is this codebase’s actual heavily-adopted loading indicator, and has no page in this documentation yet; see the warning above
Last modified on September 7, 2026