Skip to content
txbrendan/ui

Progress Log

beta

The steps of a long piece of work, like an import, a setup, or a deploy. The list grows while it runs, then folds into one line, like Done in 34s, 5 steps. A failure stays open and says why.

Preview

live

  1. Uploaded contacts.csv (working)

    2,400 rows, 312 KB

Uploaded contacts.csv

loading

  1. Built 48 pages

  2. Resizing images (working)

Resizing images

done

Done in 34s, 5 steps

opened

  1. Uploaded contacts.csv

    2,400 rows, 312 KB

    3s
  2. Checked every row

    12s
  3. Merged 64 duplicates

    9s
  4. Added tags from the Source column

    4s
  5. Saved 2,318 contacts

    6s
Done in 34s, 5 steps

failed

The card was declined. Try another card.

  1. Made the account

  2. Picked the Team plan

  3. Charging the card (failed)

Failed at step 3 of 4, after 9s

summary

Set up your workspace

Install

  1. 1. Add the registry to components.json, once per app. Without it, the shadcn CLI looks up @txbrendan in a public list that txbrendan/ui does not control.

    npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"
  2. 2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.

    npx shadcn@latest add @txbrendan/theme
  3. 3. Add the component.

    npx shadcn@latest add @txbrendan/progress-log

Use it for

  • Show the steps of work that takes a while, like importing a file, setting up an account, or publishing a site.
  • Pass loading while it runs. Add each step to the end of steps as it starts. The last one spins.
  • Set loading to false when it is done. The list folds into one line that opens again.
  • Pass total when you know how many steps there are. The line then counts them, like Step 3 of 5.
  • If it stops, set loading to false and pass error in plain words. The last step turns red, and the list stays open.
  • Pass duration, like 34s, and keep it current while loading for a live timer. The folded line uses it.
  • Give each step an id, so a new one fades in and the rest stay still.

Avoid it for

  • A single wait with no steps. Use Spinner.
  • One job with its details and result. Use JobCard.
  • Progress you can measure, like 3 of 5 files sent, with no steps to show. Use Progress.
  • An agent's own steps while it thinks. Use ThinkingState.

Props

  • steps

    Required. The steps so far, oldest first. While loading, the last one is under way.

    { id?: string; label: ReactNode; detail?: ReactNode; time?: string }[]

  • loading

    The work is still going. The list stays open and grows. When it turns false, the list folds into one line.

    boolean · default false

  • total

    How many steps there will be. The line counts them while loading, like Step 3 of 5.

    number

  • duration

    How long it has run, or ran in all, like "34s".

    string

  • error

    The work stopped on its last step. Say why in plain words. It shows under the line, and the list stays open.

    ReactNode

  • label

    The line while loading. It pulses slowly.

    ReactNode · default "Working"

  • summary

    The folded line once done. Default "Done in 34s, 5 steps", or "Failed at step 3 of 5, after 34s" with an error.

    ReactNode

  • defaultOpen

    Starts open or folded. Default: open while loading or failed, folded when done.

    boolean

  • open

    Controls whether it is open. Pair it with onOpenChange. Then it no longer folds itself.

    boolean

  • onOpenChange

    Called when it opens or folds.

    (open: boolean) => void

  • className

    Extra classes on the root. Later classes win.

    string

Accessibility

  • The line is one button that opens and folds the steps, and it tells screen readers if it is open.
  • The steps are an ordered list named Steps. The one under way says (working), and a failed one says (failed), to screen readers.
  • Each new step is said politely in a hidden status region, and so is the summary when the work ends.
  • A failure is shown three ways: the words Failed at, an icon, and the danger color. The reason is plain text under the line.
  • The spinners and the pulse stop when the theme turns motion off, and for reduced motion. The list then opens and folds at once.

Examples

While running

<ProgressLog
  loading
  total={4}
  duration="12s"
  steps={[
    { id: "upload", label: "Uploaded contacts.csv", detail: "2,400 rows" },
    { id: "check", label: "Checking each row" },
  ]}
/>

Done

<ProgressLog
  duration="34s"
  steps={[
    { id: "upload", label: "Uploaded contacts.csv" },
    { id: "check", label: "Checked 2,400 rows" },
    { id: "merge", label: "Merged 64 duplicates" },
  ]}
/>

Failed

<ProgressLog
  total={4}
  duration="9s"
  error="Row 214 has no email. Fix it and import again."
  steps={[
    { id: "upload", label: "Uploaded contacts.csv" },
    { id: "check", label: "Checking each row" },
  ]}
/>