Skip to main content

spinner — busy indicator

UiSpinner is a spinning glyph, optionally with a label. Use it as a placeholder for content that has not arrived yet — an empty list body, a card that is still fetching — or inline next to a status line.

This is the declarative spinner: a real node you put in the tree and later replace with a patch once the data arrives. It is distinct from the automatic busy feedback the event bus paints on the control you just clicked, which needs no node at all.

Live — the three sizes, two labelled spinners, and (bottom) a busy button, which is an action's loading state rather than a spinner node.

Fields

FieldTypeMeaning
idStringNode id — the DOM id and the patch target you replace once loading finishes.
sizeSM · MD · LGGlyph size. Defaults to MD when absent.
labelStringOptional visible text next to the glyph (e.g. "Loading…").
titleStringAccessible name for role="status". Falls back to label; with neither, the spinner is aria-hidden (decorative).
cssClassStringExtra CSS class on the element.

Building one

UiSpinner.of(); // bare, decorative
UiSpinner.of("Loading…"); // with a visible label
UiSpinner.of().size(UiSpinner.Size.LG); // large
UiSpinner.of().size(UiSpinner.Size.SM).label("Importing products…");

// Placeholder now, real content later — same id, replaced by a patch:
UiPatch.of().patch(UiPatch.Operation.replace("panel", loadedTable()));

Notes

You usually don't need one. Any control that dispatches a trigger — a UiAction button, an action rendered as a link, a form submit — gets an is-loading class from the bus for the duration of its request, which the CSS paints as a small spinner and which blocks a second click. That covers "this click is in flight" without a node. Suppress it for a page with bus.setLoadingPolicy("manual").

Reach for a node when the region is loading, not the control. A panel that fetches on mount, a table body waiting on a slow query, a card in a dashboard — put a UiSpinner there with a stable id and REPLACE it with the real content when it arrives.

And reach for loading when the server owns the state. An action with loading: true renders busy and disabled across re-renders, which is what you want when the work outlives the request that started it. See action.

Give it a name or make it silent. title (or the visible label) becomes the accessible name behind role="status"; with neither, the spinner is marked aria-hidden and screen readers skip it. Both are correct — pick deliberately.

Colour and size follow the surroundings. The glyph uses currentColor and the size modifier only; set color on an ancestor to tint it.

See also