Skip to content

Progress

A determinate bar showing how far a known-length task has advanced.

Stablev0.4.2added in v0.1.0@garn/ui/progress
On this page

Default

A labeled determinate bar at 60%.

Installation

garn is copy-in — the garn CLI writes this component's source into your project, so you own and can edit it. It resolves any base files and installs the npm dependencies below for you.

terminal
npx garn-ui add progress
First time? Set up garn in your project
Registry
progress
Deps
@radix-ui/react-progressclass-variance-authority
Registry deps
utils

Import

import { Progress } from "@garn/ui/progress";

Props

The component's public props and their types.

format
Description
Format the read-out + `aria-valuetext`.
Type
(value: number, max: number) => string
Default
(value, max) => `${Math.round((value / max) * 100)}%`
indeterminate
Description
Force indeterminate even with no value (the unknown-progress mode).
Type
boolean
Default
false
label
Description
A visible caption (rendered in a ProgressLabel; becomes the accessible name).
Type
React.ReactNode
showValue
Description
Show the value read-out beside the label.
Type
boolean
Default
false
size
Description
Track height.
Type
"lg" | "md" | "sm"
Default
"md"
thresholds
Description
Ascending value→tone map for `tone="auto"`.
Type
ProgressThreshold[]
Default
[{ at: 0, tone: "success" }, { at: 75, tone: "warning" }, { at: 90, tone: "danger" }]
tone
Description
`neutral | brand | success | warning | danger | info`, or `"auto"` to drive the tone from the value via `thresholds`.
Type
"auto" | ProgressTone
Default
"brand"
value
Description
0–`max`. `null`/`undefined` → indeterminate (or set `indeterminate`).
Type
number

Plus 282 inherited native <div> attributes.

Styling

Target these data-slots and states, and remap these tokens, to restyle without forking the component.

[data-slot="progress"]

States

SelectorState
[data-state=indeterminate]indeterminate
[data-state=loading]loading
[data-state=complete]complete

When to use

Show determinate progress of a task with a known percentage — upload, install, multi-step completion.

Reach for something else when

  • An indeterminate wait with no measurable percentage (use a spinner)
  • a static ratio or score (use plain text or a meter).

Overview

Progress shows the determinate progress of a task with a known percentage — an upload, an install, multi-step completion — as a filled bar with a tone that can shift at thresholds, and it doubles as a capacity meter (storage used, quota). Use it when you can report a real fraction; for an indeterminate wait use a Spinner, and to hold a content shape while loading use a Skeleton.

Guidelines

Use Progress only when you know the fraction. A determinate bar that actually advances reassures users; faking it (or snapping 0→100) erodes trust. For unknown-duration work, a Spinner is the honest choice.

Pair the bar with a real number and context. Show the percentage or "3 of 5", and label what's progressing — a bar alone leaves users guessing. As a capacity meter, make the tone shift meaningful (e.g. warning as a quota fills).

Wire it for assistive tech. Expose the value via the progress role (aria-valuenow/min/max) and an accessible name so the state is announced, not just shown. Keep updates smooth and avoid jarring jumps; respect reduced-motion on any animation.

Best practices

Do
  • Pass value as 0–100 and give the bar an accessible name (aria-label or aria-labelledby).
  • Pair it with a visible percentage or step label when the number matters.
Don't
  • Don't use Progress for unknown-duration loading — that's a Spinner.
  • Don't animate a fake value just to look busy.

Content guidelines

  • Give it an accessible label naming the task (“Uploading photo”).
  • When you show a percentage or count, keep it true to the real value.

Troubleshooting

The bar renders but is announced with no name.

Cause. Progress has no text content of its own.

Fix. Pass aria-label (or aria-labelledby pointing at a visible label).

Accessibility

Role
progressbar
A1.3.1Info and RelationshipsA4.1.2Name, Role, Value
  • Radix sets aria-valuenow / aria-valuemin / aria-valuemax from `value` and `max`.
  • The bar carries no text — give it an accessible name via aria-label or aria-labelledby.