Spinner
Indeterminate loading indicator - a single rotating glyph that fits anywhere text fits.
Overview
Spinner is the indeterminate loading primitive: a single rotating glyph (the lucide Loader2 geometry) for in-flight work of unknown duration. Because it is just a glyph, it lives anywhere text fits - inline next to a label, inside a button while a request is pending, in an empty state.
Reach for Spinner when you cannot say how long the wait will be. When the percent complete is known, use Progress; when you are reserving layout for the shape of loading content, use Skeleton. Keep it small and local - a full-page spinner is usually a Skeleton layout instead.
Preview
'use client';import { Spinner } from '@gremorie/rx-feedback';export function SpinnerPreview() { return ( <div className="flex items-center gap-6"> <Spinner size="sm" /> <Spinner /> <Spinner size="lg" /> </div> );}Anatomy
Spinner single rotating Loader2 glyph (role="status", aria-live="polite", aria-label="Loading")Installation
# React
npm i @gremorie/rx-feedback
# Angular
npm i @gremorie/ng-feedback
npx gremorie@latest add ng-spinnerThe Angular edition ships as the ng-spinner registry item. The React
edition installs from the @gremorie/rx-feedback npm package; a standalone
registry item is not published yet.
Usage
import { Spinner } from "@gremorie/rx-feedback";
export function LoadingRow() {
return (
<p className="flex items-center gap-2 text-sm text-muted-foreground">
<Spinner size="sm" />
Loading conversations...
</p>
);
}npx gremorie@latest add ng-spinnerimport { Component } from '@angular/core';
import { Spinner } from '@gremorie/ng-feedback';
@Component({
selector: 'app-loading-row',
standalone: true,
imports: [Spinner],
template: `
<p class="flex items-center gap-2 text-sm text-muted-foreground">
<gr-spinner size="sm" />
Loading conversations...
</p>
`,
})
export class LoadingRowComponent {}API
<Spinner>
React renders the lucide Loader2Icon with animate-spin; Angular (<gr-spinner>) renders an inline SVG with the same geometry. The public surface is identical in both editions.
| Prop | Type | Default | Description |
|---|---|---|---|
size | "sm" | "default" | "lg" | "default" | Glyph size: 12 / 16 / 24 px. |
className | string | - | Extra classes, e.g. a text color. In Angular this maps to the host class. |
...props | React.ComponentProps<"svg"> | - | Standard SVG attributes (React edition). role, aria-live, and aria-label are already set for you. |
Composition
- The glyph strokes
currentColor, so it tints with the surrounding text color - settext-muted-foreground(or any text token) on the Spinner or its parent and the glyph follows. - Inline with text: pair
size="sm"with a short label ("Loading conversations...") inside a flex row. The glyph aligns like a leading icon. - Inside a button: drop a Spinner before the label while a request is in flight; disable the button at the same time so the pending state reads as one signal.
- Empty states: center a
size="lg"Spinner while the first page of data loads, then swap in the real content or an empty-state block.
Variations
Inline with text
Loading conversations...
'use client';import { Spinner } from '@gremorie/rx-feedback';export function SpinnerInlinePreview() { return ( <p className="flex items-center gap-2 text-sm text-muted-foreground"> <Spinner size="sm" /> Loading conversations... </p> );}The most common shape: a small glyph beside a short status label. The label carries the meaning; the glyph carries the motion.
Accessibility
- Announced out of the box: the component sets
role="status",aria-live="polite", andaria-label="Loading", so screen readers announce the pending state without extra wiring. - One announcement per region: when several elements load together, prefer one Spinner (or one
aria-busyregion) over many, so assistive tech gets a single status instead of repeated chatter. - Reduced motion: the rotation uses Tailwind's
animate-spin, which respectsprefers-reduced-motionvia the global project override. - Color contrast: the glyph inherits
currentColor; keep the surrounding text color at AA contrast and the Spinner follows.