Skip to content
FeedbackGenerated from the public API

Progress

A determinate or indeterminate bar for work whose size is known.

@luc/ui

Examples

A bar that is actually progressing

The value lives in the caller, and the bar's own width transition is what moves — no second animation is written beside the component.

Ingest paused

62 %

At the ends, and in the middle

Zero is a value, not an absence: a bar at 0 per cent is determinate and announces `aria-valuenow="0"`.

Not started

0 %

Running

62 %

Finished

100 %

Indeterminate

Omitting `value` is the honest answer when the length is genuinely unknown — and it is the one mode where the component owns the motion.

Reconnecting to the stream

—

Connecting to the provider

—

Two sizes, four tones

`size` is the bar's height, not the label's: `sm` is the one that fits inside a table row or a toolbar.

Ingest · accent · md

48 %

Validated · positive · md

91 %

Quota · warning · sm

78 %

Rejected · negative · sm

12 %

In a task row

Where a bar actually lives: beside a status and a sentence about what is running, with the action that stops it.

Sector ingest

Pending

18 of 44 sectors · about 6 minutes left

Started 12:04 UTC · writes to luc-data/research

Usage

import { Progress } from "@luc/ui";
<Progress value={62} aria-label="Tasks complete" />

Omit `value` for the indeterminate sweep; the bar then exposes the role without a value, which is what the pattern asks for.

Package setup — peer requirements, the stylesheet and the provider — is on installation.

API

Read from the published TypeScript API, so a prop that is not listed here does not exist and a default that is listed is the one the compiler sees.

Three props: the value (or its absence), the tone and the size. `value={null}` and no `value` at all mean the same thing — the indeterminate sweep.

Exported symbols
ExportKindSignature
Progress6 documented props({ className, value, tone, size, ...props }: ProgressProps) => import("@types/react").JSX.Element

Progress props

Type: ProgressProps

Values the compiler reads from the declaration.

Progress props
PropTypeDefaultNotes
asChild?inheritedboolean | undefined—
getValueLabel?inherited((value: number, max: number) => string) | undefined—
max?inheritednumber | undefined—
size?"sm" | "md" | undefined—
tone?"warning" | "accent" | "positive" | "negative" | undefined—
value?number | null | undefined—0–100. Omit for an indeterminate bar.

Guidelines

Do

  • Use a determinate bar when the work has a known size, and the indeterminate sweep when it genuinely does not.
  • Put the number beside the bar in the caller's own type — the component draws the track, not the label.
  • Let the width transition express the change; the reader is watching the value, not the surface.

Don't

  • Do not show a percentage you cannot compute — an invented number is worse than an honest sweep.
  • Do not use a progress bar for a wait with no measurable end; that is a spinner.
  • Do not add a second animation to the indeterminate sweep: it is already the one motion in the component.

Accessibility

Radix owns the value semantics: a determinate bar exposes `role="progressbar"` with `aria-valuenow`, `aria-valuemin` and `aria-valuemax`, and an indeterminate one exposes the role without a value. The bar has no name of its own, so give it one — `aria-label`, or `aria-labelledby` pointing at the heading it belongs to — and the indeterminate sweep is marked `data-motion-essential`, so it keeps meaning under `prefers-reduced-motion` as a slow pulse rather than freezing.

Family

Feedback

What the product says when something happened, is happening, or did not.

10 documented entries in this family. Open the family index

Luc UI v1.0Progressdark theme