Gesso
Feedback

Progress

A bar showing a value toward a total, on a tinted track, in two sizes.

Progress shows how far along one task is, like an upload or an import, as a fill that grows across a track. It wraps Radix Progress, takes a value out of max, 100 by default, and exposes it as a progressbar. The fill is bg-tone, the primary unless you set a tone, on a track that is an 8% wash of the same color.

Usage

import { Progress } from '@/components/ui/progress';

<Progress value={uploaded} aria-label="Upload" />

Fill and motion

The fill is sized by its width, as a percentage of the track, not by a transform. When value changes it eases to the new width, and under reduced motion it jumps. Click Start to run it.

0%

A value that is missing or not a number draws an empty fill, and one out of range is logged by Radix as an error. Keep it between 0 and 100, and see Accessibility for what is exposed when there is no value.

Two sizes

md is the default, a 16px track with the fill inset 4px. sm is an 8px track with the fill inset 2px, the same proportions at half the size, for a thin bar in a table row or under a stat. Use size rather than a height class, since the inset is part of the size.

Medium, 16px
Small, 8px
<Progress value={41} size="sm" aria-label="Sent" />

Props

Progress takes the props of Radix Progress's root, plus this.

PropTypeDefaultWhat it does
tone"neutral" | "primary" | "support" | "danger" | "success" | "warning" | "info" | "inherit""primary"The hue of the fill and its track, such as success for a finished upload. inherit takes the tone of the surrounding scope.
size"sm" | "md""md"An 8px or a 16px track, with the fill inset by a quarter of the height.
valuenumber | nullHow far along it is, out of max. Sets aria-valuenow and the fill width. null is indeterminate.
maxnumber100The value that fills the track.

Accessibility

  • The root has role="progressbar", with aria-valuemin="0", aria-valuemax, and aria-valuenow set to value. The default aria-valuetext is the rounded percent, like "30%", and getValueLabel changes it.
  • A bar has no name of its own, so give every one an aria-label, or point aria-labelledby at a visible label, so the value is announced with a name.
  • A null or missing value makes Radix drop aria-valuenow and set data-state="indeterminate", which is how a screen reader hears an unknown amount. Progress sweeps a segment across the track for that state, and under reduced motion it holds still as a tint across the whole track, so it does not read as a fixed amount.
  • Changes are not announced as they happen. A screen reader reads the value when the bar is focused or browsed to, so announce the result of the task, like "Import finished", in a live region or a Toaster.
  • The bar is not focusable and takes no input.
  • Under reduced motion the fill changes without a transition.
  • There is no forced-colors rule. The fill and the track are background colors, which the browser replaces there, so the bar may not draw while aria-valuenow still carries the value.

When to use something else

  • Segmented bar for a whole split into parts, like delivered, bounced, and not sent. It is a snapshot of a distribution and Progress is one value moving toward a total.
  • Skeleton for content that is loading, when the shape is known and the progress is not.
  • A Button's loading for an action that started at that button and has no useful percentage. It disables the button and shows a spinner in place.
  • Stepper for progress through named stages rather than a quantity.

Install

shadcn add @gesso/progress

On this page