Installation
pnpm dlx shadcn@latest add https://asciicn.dev/r/loader.jsonUsage
import { Loader } from "@/components/asciicn/loader";
export function Saving() {
return <Loader size="sm" color="blue" label="Saving changes" />;
}The default is a one-cell spinner that cycles the orbit sequence, ⣾⣽⣻⢿⡿⣟⣯⣷. Pick another with spinner: braille (⠋⠙⠹⠸), arc (◜◠◝◞), tick (-\|/), bar (▁▂▃▄▅▆▇█) or quadrant (▖▘▝▗). Other variants are wider:
dots: a bright●steps along three dots, leaving a half-lit•behind it.wave: six eighth blocks ride one sine wave, each a step behind its neighbour.comet: a Braille comet with a full head and a tail of single dots crosses six cells.
Inside a button, pass color="currentColor" so the glyphs follow the button's text.
Examples
Every sequence
All six spinners and the three wider variants at size="lg".
In context
A deploy row with a spinner, a log stream with a small wave and its label shown, and an empty event feed with a comet and a disabled button that holds a spinner.
Props
| Prop | Type | Default |
|---|---|---|
| label What is loading. Screen readers announce it politely. | Type: string | Default "Loading" |
| showLabel Show the label as text next to the glyphs. | Type: boolean | Default false |
| variant The animation. | Type: "spinner" | "dots" | "wave" | "comet" | Default "spinner" |
| spinner For the spinner variant: which glyph sequence to cycle. | Type: "orbit" | "braille" | "arc" | "tick" | "bar" | "quadrant" | Default "orbit" |
| size Glyph cell height: 12, 16 or 24px. | Type: "sm" | "md" | "lg" | Default "md" |
| color Glyph colour, a palette name or any CSS colour such as "currentColor". | Type: AsciiColor | Default "ink" |
| speed Playback speed multiplier. 0 holds a still frame. | Type: number | Default 1 |
| cell Glyph cell height in CSS pixels. Overrides size. | Type: number | Default – |
Notes
- The root is a polite live region (
role="status"), so a screen reader readslabelonce when the loader appears. The glyphs arearia-hidden. - The width is fixed in cells (1 for a spinner, 3 for dots, 6 for the wave and comet), so text next to it never shifts as frames change.
- Frames are written straight into the text node, only when the glyphs change, with no React render per frame. The loop pauses offscreen and in hidden tabs.
- With reduced motion the loader holds one frame chosen to read as busy on its own: a full orbit glyph, the middle dot lit, a wave crest, the comet mid-flight.