1
ComponentsFeedback

Progress

Determinate progress with labels, formatted values, sizes and a token-driven indicator transition.

Purpose

Use Progress for measurable work with a known current value and maximum, such as uploads, imports or multi-step processing.

Task progress

Deploy progress
x
import { Progress, ProgressIndicator, ProgressLabel, ProgressTrack, ProgressValue } from "@/components/ui/progress";

export function Example() {
  return (
    <Progress value={64}>
      <div><ProgressLabel>Deploy</ProgressLabel><ProgressValue /></div>
      <ProgressTrack><ProgressIndicator /></ProgressTrack>
    </Progress>
  );
}

Installation

With the Siteplane registry alias configured, add the component and import the installed source from your app.

CLI

pnpm dlx shadcn@4.16.1 add @siteplane/progress

Import

import { Progress } from "@/components/ui/progress";
import { ProgressIndicator, ProgressLabel, ProgressTrack, ProgressValue } from "@/components/ui/progress";

Need the machine-readable source metadata? View registry JSON.

Usage

Progress communicates completion of a task. Pair the track with a label when the number matters.

import { Progress, ProgressIndicator, ProgressLabel, ProgressTrack, ProgressValue } from "@/components/ui/progress";

export function Example() {
  return (
    <Progress value={64}>
      <div><ProgressLabel>Deploy</ProgressLabel><ProgressValue /></div>
      <ProgressTrack><ProgressIndicator /></ProgressTrack>
    </Progress>
  );
}

Examples

Labels, formatted values, sizes, value states and indeterminate loading are props and compositions of the same primitive.

Label and value

Export data
x
<Progress value={60}>
  <div className="flex items-center justify-between gap-2">
    <ProgressLabel>Export data</ProgressLabel>
    <ProgressValue />
  </div>
  <ProgressTrack>
    <ProgressIndicator />
  </ProgressTrack>
</Progress>

Formatted value

ProgressValue accepts a render function when percent is not the right unit.

Upload
x
<Progress aria-valuetext="Upload 502 / 512" max={512} value={502}>
  <div className="flex items-center justify-between gap-2">
    <ProgressLabel>Upload</ProgressLabel>
    <ProgressValue>{(_formatted, value) => `${value} / 512`}</ProgressValue>
  </div>
  <ProgressTrack>
    <ProgressIndicator />
  </ProgressTrack>
</Progress>

Sizes

sm and default share the same track height — the root gap and value text scale with the size.

Small
x
Default
x
Large
x
Extra large
x
<Progress size="sm" value={64}>...</Progress>
<Progress value={64}>...</Progress>
<Progress size="lg" value={64}>...</Progress>
<Progress size="xl" value={64}>...</Progress>

Value states

Without children the root renders track and indicator automatically.

x
x
{/* Low */}
<Progress aria-label="Import progress" value={12} />

{/* Complete */}
<Progress aria-label="Export complete" value={100} />

Indeterminate

value={null} switches to the indeterminate state; aria-valuetext keeps the status readable.

Preparing exportPending
x
<Progress aria-valuetext="Preparing export files" value={null}>
  <div className="flex items-center justify-between gap-2">
    <ProgressLabel>Preparing export</ProgressLabel>
    <span className="text-muted-foreground text-xs">Pending</span>
  </div>
  <ProgressTrack>
    <ProgressIndicator />
  </ProgressTrack>
</Progress>

Auto updating

An indeterminate bar with the continuous fill utility for background work without a known total.

x
<Progress aria-label="Auto updating upload progress" value={null}>
  <ProgressTrack>
    <ProgressIndicator className="siteplane-progress-continuous-fill h-full w-full" />
  </ProgressTrack>
</Progress>

Circular

A custom SVG visualization on top of Progress semantics — the primitive keeps the accessible value.

25%
Syncing workspacex
const radius = 27;
const circumference = 2 * Math.PI * radius;
const dashOffset = circumference * (1 - value / 100);

<Progress aria-label="Sync progress" className="items-center" value={value}>
  <div className="relative grid size-16 place-items-center">
    <svg aria-hidden="true" className="absolute inset-0 size-full -rotate-90" viewBox="0 0 64 64">
      <circle className="stroke-input" cx="32" cy="32" fill="none" r={radius} strokeWidth="7" />
      <circle
        className="stroke-primary transition-[stroke-dashoffset] duration-(--motion-duration-linger) ease-(--motion-ease-soft-in-out)"
        cx="32"
        cy="32"
        fill="none"
        r={radius}
        strokeDasharray={circumference}
        strokeDashoffset={dashOffset}
        strokeLinecap="round"
        strokeWidth="7"
      />
    </svg>
    <div className="relative grid size-11 place-items-center rounded-full bg-background font-medium text-sm tabular-nums">
      {value}%
    </div>
  </div>
  <ProgressLabel className="text-center text-muted-foreground text-xs">
    Syncing workspace
  </ProgressLabel>
</Progress>

API Reference

The reference lists the source-owned exports and props that are easy to miss in visual examples. Inherited Base UI props remain available unless the wrapper narrows them.

  • Progress accepts a numeric value or value={null} for indeterminate status, plus max, format, locale and size="sm" | "default" | "lg" | "xl"; the wrapper defaults locale to en-US.
  • Compose ProgressLabel, ProgressValue, ProgressTrack and ProgressIndicator when custom children are needed.
  • ProgressLabel, ProgressValue and ProgressTrack each accept their own size override; otherwise they inherit the root or provider size.
  • Use getAriaValueText or aria-valuetext when the numeric value needs a human-readable explanation.

Motion

Component motion uses the shared Siteplane motion contract. See the global motion guide for provider setup, tokens and reduced-motion behavior.

  • Value changes animate through the shared Progress transition tokens; the surrounding layout remains stable.
  • The operating-system reduced-motion preference removes the indicator transition. The forced reducedMotionPreset shortens the shared linger duration from 380ms to 120ms, and the provider contract stops the continuous indeterminate fill.
  • Do not add local durations or transforms; state changes and progress semantics remain visible in every mode.

Accessibility

  • Provide a visible label or explicit accessible name and keep value and max accurate.
  • Use human-readable value text for units or stages that a plain number cannot explain.
  • Use a numeric value for known progress. value={null} supports a short transitional indeterminate state when paired with useful aria-valuetext; Spinner or Skeleton can be clearer for unknown or long-running loading.

Implementation Guidance

  • Keep track and indicator together when supplying custom children.
  • Set locale deliberately for non-English products instead of relying on the en-US wrapper default.
  • Use part-level size overrides only when a real composition requires them; root size is the normal path.

On This Page