Skip to content

Actions

Progress Bar

A fully styleable progress indicator for how much of a task is done.

When to use it: Use it while work completes over time, such as an upload.

On this page

Example

40% complete

Progress Bar.tsxtsx
import { useState } from "react";
import { Label } from "@comp0/react";
import { ProgressBar } from "@comp0/react";

export function Example() {
  const [value, setValue] = useState(0.4);

  return (
    <div className="flex max-w-xs flex-col gap-2">
      <Label
        id="upload-progress-label"
        className="text-base font-medium text-zinc-900 sm:text-sm dark:text-zinc-100"
      >
        Uploading photos
      </Label>
      <ProgressBar
        id="upload-progress"
        value={value}
        aria-labelledby="upload-progress-label"
        className="h-2 w-full overflow-hidden rounded-full bg-zinc-200 dark:bg-zinc-800"
      >
        <span className="block h-full w-[calc(var(--comp0-progress-value)*100%)] rounded-full bg-teal-700 transition-[width] dark:bg-teal-400" />
      </ProgressBar>
      <p className="text-base text-zinc-600 tabular-nums sm:text-sm dark:text-zinc-400">
        {Math.round(value * 100)}% complete
      </p>
      <button
        type="button"
        className="self-start rounded bg-teal-700 px-3 py-2.5 text-base text-white sm:py-2 sm:text-sm dark:bg-teal-400 dark:text-zinc-950"
        onClick={() => setValue((current) => Math.min(1, current + 0.2))}
      >
        Advance
      </button>
      <ProgressBar
        aria-label="Preparing download"
        className="h-2 w-full overflow-hidden rounded-full bg-zinc-200 dark:bg-zinc-800"
      >
        <span className="block h-full w-1/3 animate-pulse rounded-full bg-teal-700 motion-reduce:animate-none dark:bg-teal-400" />
      </ProgressBar>
    </div>
  );
}

Anatomy

Dashed frames are invisible state providers; shaded shapes own real DOM. Numbered pins match the list below.

A wireframe sketch of the assembled component. Each numbered marker matches a part in the list that follows.

  1. ProgressBar

    Styleable div with progressbar semantics and an optional custom fill. Owns a DOM element.

Step by step

  1. 1

    Add the main part

    Add ProgressBar where the task's status belongs.

  2. 2

    Add the supporting parts

    Name it with a wired Label or an aria-label.

  3. 3

    Make the behavior clear

    Pass value as the completed fraction of max, or omit value while the total is unknown; add your own fill and size it with --comp0-progress-value.

    Exampletsx
    <ProgressBar aria-label="Uploading photos" value={0.4}><span className="fill" /></ProgressBar>

Keyboard

Forms and accessibility

No form behavior; progress reports status and submits nothing.

Accessibility checklist

  • Always name the bar with a Label or an aria-label.
  • Show the percentage as visible text when precision matters.
  • Use Meter instead for a measurement that is not task progress.

API reference

Importtsx
import { ProgressBar } from "@comp0/react";

ProgressBar

DOM element

Styleable div with progressbar semantics and an optional custom fill.

PropTypeDescription
valuenumberCompleted amount between 0 and max; omit it for an indeterminate bar.
maxnumberUpper bound of the range; defaults to 1.
childrenReactNode | (state: ProgressBarState) => ReactNodeCustom track contents or a render function receiving value, max, and percentage.
aria-labelstringNames the bar when it is not labelled by visible text.
aria-labelledbystringPoints to the visible text that names the bar.
aria-valuetextstringExplains a determinate value when the number alone is not meaningful, such as “3 of 5 files”.

Style hooks

Attributes that appear while a state is true. Target them with Tailwind data variants such as data-open:bg-zinc-100, or with any CSS selector.

[data-indeterminate]

on ProgressBar

No value was given; the bar shows unknown progress.
--comp0-progress-value

on ProgressBar

Normalized 0–1 value for sizing a custom fill.

Keep exploring