Skip to main content

progress — bars and rings

UiProgress shows how far along a task is, as a horizontal BAR (the default) or a circular CIRCLE ring. Set value for determinate progress; leave it out and the node renders an indeterminate animation for "work is happening, duration unknown".

status tints the fill, so the same node doubles as the result state once the task finishes — a bar that ends green, or red.

Live — determinate bars with status colours, an indeterminate bar, three rings. "Advance +20%" drives the top bar and the first ring with a REPLACE patch, exactly as a server would.

Fields

FieldTypeMeaning
idStringNode id — the DOM id and the target of the REPLACE patch that advances it.
valueDoubleCurrent progress. null renders an indeterminate loop.
maxDoubleUpper bound for value. Defaults to 100 when absent.
variantBAR · CIRCLEShape. Defaults to BAR.
statusNORMAL · SUCCESS · WARNING · ERRORColour intent of the fill. Defaults to NORMAL.
showValueBooleanWhether to render the trailing NN% readout. Defaults to true; forced off while indeterminate.
titleStringAccessible label on the role="progressbar" element.
cssClassStringExtra CSS class on the element.

Building one

UiProgress.of(60); // a 60% bar
UiProgress.of(30, 120); // 30 of 120 → 25%
UiProgress.of(100).status(UiProgress.Status.SUCCESS); // green, done
UiProgress.of(80).status(UiProgress.Status.WARNING).showValue(false);
UiProgress.of(45).variant(UiProgress.Variant.CIRCLE); // a ring
UiProgress.indeterminate(); // duration unknown
UiProgress.indeterminate().variant(UiProgress.Variant.CIRCLE);

// Advancing it is an ordinary patch on the node's id:
UiPatch.of().patch(UiPatch.Operation.replace("upload", UiProgress.of(80)));

Notes

Omitting value is the switch. There is no indeterminate flag — a null value is indeterminate, and UiProgress.indeterminate() is just a constructor that leaves it unset. The renderer then drops the NN% readout and swaps aria-valuenow for aria-busy="true".

Advance it with a REPLACE patch, keeping the id. Progress is server state: each update is an ordinary patch on the same node id, from a polling trigger, a streamed response, or a handler. Nothing is animated for you between values — the CSS transitions the fill width, so coarse steps still look smooth.

status turns the bar into an outcome. SUCCESS at 100 reads as "done", ERROR as "failed at 45%". That saves swapping the node for a text line when the task settles.

percent is computed, not carried. The readout is round(value / max * 100) clamped to 0–100, so a value of 30 against max 120 shows 25%. max defaults to 100, which is why UiProgress.of(60) already means 60%.

For an unknown and uninteresting duration, use a spinner instead. An indeterminate bar claims a full-width slot; spinner is the lighter signal, and a control's own busy state is lighter still.

See also