Loading…
Loading…
Indicates the completion progress of a task or operation.
The Progress Bar (also called a loading bar, progress indicator, or meter) is a feedback component that visually communicates the completion status of a task or operation. It answers the most fundamental question a user has during any wait: "How much longer?"
Progress bars reduce perceived wait time by giving users a sense of forward momentum. Research by Jakob Nielsen and others consistently shows that providing progress feedback — even approximate — makes users perceive waits as 10–20% shorter than identical waits with no indicator. This isn't just UX polish; it's a measurable reduction in abandonment rates for file uploads, multi-step forms, and data processing workflows.
When to use a Progress Bar:
When NOT to use a Progress Bar:
Determinate vs. Indeterminate: A progress bar can be determinate (0%→100% with known progress) or indeterminate (animated loop indicating "something is happening" without quantifying it). If you find yourself using an indeterminate progress bar, ask whether a Spinner would be more appropriate — indeterminate bars are most useful when the operation will eventually become determinate (e.g., waiting for a server to calculate the total before showing real progress).
Use our Color Palette Generator to pick semantically meaningful fill colors (blue for neutral progress, green for completion, red for quota warnings). Preview your bar's animation easing with the Animation & Easing Tool. Verify label contrast with the Contrast Checker.
| Variant | Visual Treatment | Use Case |
|---|---|---|
| Linear | Horizontal bar, left-to-right fill. The classic progress bar. | File uploads, form completion, quota meters |
| Circular / Radial | Ring or arc that fills clockwise. | Dashboard widgets, profile completion, compact spaces |
| Semicircular / Gauge | Half-circle arc, often with a needle or fill. | Performance scores, speed indicators, health metrics |
| Segmented | Bar divided into discrete steps. | Multi-step processes where each step is a distinct phase |
| Stacked / Multi-part | Multiple colored segments within one bar. | Storage breakdowns (photos, videos, documents), budget allocation |
| Thin / Slim | 2–4px bar, typically at the very top of the page. | Page load indicators (YouTube, GitHub), background operations |
| Variant | Color | Purpose |
|---|---|---|
| Default | Brand blue or neutral | Standard operations with no semantic meaning |
| Success | Green | Completion state or healthy quota levels |
| Warning | Amber/Yellow | Approaching limits (80%+ of quota) |
| Danger | Red | Exceeded limits or failed operations |
| Size | Height | Label Placement | Use Case |
|---|---|---|---|
| xs | 2–4px | External or none | Page-level loading bars, minimal footprint |
| sm | 6–8px | External only | Inline progress in lists or cards |
| md | 12–16px | Internal or external | Default for most use cases |
| lg | 20–24px | Internal (label inside bar) | Hero progress displays, dashboards |
transition or a spring animation. Preview easing curves with the Animation & Easing Tool.@keyframes with translateX for performance (GPU-accelerated).| Property | Type | Default | Description |
|---|---|---|---|
value | number | 0 | Current progress value (0–100, or custom range). Required for determinate bars. |
max | number | 100 | Maximum value for the progress range |
min | number | 0 | Minimum value (usually 0) |
indeterminate | boolean | false | When true, displays a looping animation with no specific value |
variant | 'default' | 'success' | 'warning' | 'danger' | 'default' | Semantic color variant |
size | 'xs' | 'sm' | 'md' | 'lg' | 'md' | Controls bar height |
label | string | ReactNode | — | Text displayed near or inside the bar |
showValue | boolean | false | Displays the current percentage or value text |
valueFormat | (value: number, max: number) => string | — | Custom formatter for displayed value (e.g., "3 of 5 steps") |
striped | boolean | false | Applies animated diagonal stripes to the fill |
animated | boolean | true | Whether the fill width transitions smoothly |
color | string | — | Custom fill color, overriding the variant. Generate palettes with the Color Palette Generator. |
trackColor | string | — | Custom track (background) color |
borderRadius | string | number | — | Override border-radius. Preview with the Border Radius Generator. |
aria-label | string | — | Accessible label when no visible label is present. Required for icon-only or unlabeled bars. |
aria-labelledby | string | — | ID of an element that labels this progress bar |
Important: For determinate progress bars, always provide a meaningful value. Screen readers announce progress changes; jumping from 0% to 100% instantly defeats the purpose. Update in reasonable increments (1–5%) for smooth, informative feedback.
Progress bars are deceptively token-rich — they touch color, spacing, motion, and border-radius. For a deep understanding of token architecture, see our Design Tokens Complete Guide.
| Token Category | Token Example | Progress Bar Usage |
|---|---|---|
| Color – Fill | --color-primary-500 | Default fill color |
| Color – Success | --color-success-500 | Success variant fill |
| Color – Warning | --color-warning-500 | Warning variant fill |
| Color – Danger | --color-error-500 | Danger variant fill |
| Color – Track | --color-neutral-200 | Track (unfilled) background |
| Color – Label | --color-on-primary | Label text inside filled region |
| Border Radius | --radius-full (9999px) | Pill-shaped bar ends. Preview with Border Radius Generator. |
| Spacing – Height | --size-2 / --size-3 / --size-4 | Bar height per size variant |
| Spacing – Gap | --space-2 | Gap between bar and external label |
| Transition | --duration-normal (300ms), --ease-out | Fill width animation. Preview with Animation & Easing Tool. |
| Shadow | --shadow-inner-sm | Optional inset shadow on the track for depth |
Many progress bars shift color as they approach thresholds. Map these to semantic tokens:
/* Auto-color based on value */
--progress-fill: var(--color-primary-500); /* 0–69% */
--progress-fill-warning: var(--color-warning-500); /* 70–89% */
--progress-fill-danger: var(--color-error-500); /* 90–100% */
This pattern is common for storage quotas, bandwidth meters, and capacity indicators. Generate all three scales from a single hue using the Color Palette Generator.
| State | Visual Behavior | Implementation |
|---|---|---|
| Empty (0%) | Track only, no fill visible. Label may show "0%" or "Not started". | value={0} — ensure the track itself is visible via background color or border. |
| In Progress | Fill width proportional to value. Smooth CSS transition between updates. | Update value in increments. Use transition: width 300ms ease-out for smooth animation. |
| Complete (100%) | Full fill, often transitions to success color. May show a checkmark icon. | Switch variant to success or show a completion message. Consider auto-hiding after a delay. |
| Indeterminate | Looping shimmer or sliding gradient animation. No numeric value shown. | Set indeterminate={true}. Use CSS @keyframes for the animation. Announce with aria-busy="true". |
| Paused | Fill stops at current position. Striped animation freezes. Visual dimming optional. | Remove animated class or set animation-play-state: paused. Update aria-label to include "paused". |
| Error / Failed | Fill turns red, may show an error icon or message. | Switch variant to danger. Provide error context via label or adjacent Alert. |
| Buffering | Two-layer fill: solid for actual progress, translucent for buffered amount. | Render two <div> fills inside the track, with the buffer fill at lower opacity. |
| Overflow | Value exceeds max (e.g., 120% of quota). Fill is fully red, label shows excess. | Clamp visual fill to 100% but display actual value in the label. Use danger variant. |
Progress bars are straightforward to make accessible, but there are important nuances around live announcements and labeling. Validate your color choices with the Contrast Checker.
Use the native <progress> element when possible — it provides built-in semantics. For custom-styled bars, use role="progressbar":
<!-- Native HTML -->
<label for="file-upload">Uploading document.pdf</label>
<progress id="file-upload" value="65" max="100">65%</progress>
<!-- Custom ARIA -->
<div role="progressbar"
aria-valuenow="65"
aria-valuemin="0"
aria-valuemax="100"
aria-label="Uploading document.pdf">
<div class="progress-fill" style="width: 65%"></div>
</div>
| Criterion | Level | Requirement for Progress Bars |
|---|---|---|
| 1.3.1 Info and Relationships (A) | A | Use <progress> or role="progressbar" with aria-valuenow, aria-valuemin, aria-valuemax. The semantic structure must convey that this is a progress indicator. |
| 1.4.1 Use of Color (A) | A | Don't rely solely on color to indicate completion/warning/error states. Include text labels, percentages, or icons alongside color changes. |
| 1.4.3 Contrast (Minimum) (AA) | AA | Fill-to-track contrast must meet 3:1 for non-text UI components. Label text must meet 4.5:1 against its background. Use the Contrast Checker. |
| 1.4.11 Non-text Contrast (AA) | AA | The progress fill must have at least 3:1 contrast against the track. This applies to all color variants, including warning (yellow on light gray can fail). |
| 2.2.1 Timing Adjustable (A) | A | If the progress bar is tied to a time limit (e.g., session expiry countdown), provide a way to extend or disable the limit. |
| 4.1.2 Name, Role, Value (A) | A | Screen readers must be able to determine the component's name (label), role (progressbar), and current value. |
| 4.1.3 Status Messages (AA) | AA | Progress updates should be announced to assistive tech. Use aria-live="polite" on a container or rely on screen reader's native progressbar polling. |
Screen readers poll role="progressbar" periodically, but the frequency varies. For critical progress (file uploads, form submissions), supplement with an aria-live region:
<div aria-live="polite" class="sr-only">
Upload progress: 65% complete
</div>
Don't announce every 1% increment — this floods the screen reader. Announce at meaningful intervals: 25%, 50%, 75%, 100%, and on error.
For indeterminate progress bars, omit aria-valuenow (don't set it to 0, which implies "no progress"). Set aria-busy="true" on the content region being loaded, and provide a text label like "Loading…":
<div role="progressbar" aria-label="Loading data…">
<!-- animated indeterminate bar -->
</div>
<div aria-busy="true">
<!-- content area being loaded -->
</div>
transition: width 300ms ease-out. Preview with the Animation & Easing Tool.<!-- Determinate Progress Bar -->
<div class="progress-bar-container">
<div class="progress-bar-header">
<span class="progress-bar-label">Uploading document.pdf</span>
<span class="progress-bar-value">65%</span>
</div>
<div class="progress-bar-track" role="progressbar"
aria-valuenow="65" aria-valuemin="0" aria-valuemax="100"
aria-label="Uploading document.pdf">
<div class="progress-bar-fill" style="width: 65%"></div>
</div>
</div>
<!-- Indeterminate Progress Bar -->
<div class="progress-bar-track progress-bar--indeterminate"
role="progressbar" aria-label="Loading data…">
<div class="progress-bar-fill"></div>
</div>
<!-- Slim Page-Top Progress Bar -->
<div class="progress-bar-top" role="progressbar"
aria-valuenow="40" aria-valuemin="0" aria-valuemax="100"
aria-label="Page loading">
<div class="progress-bar-top-fill" style="width: 40%"></div>
</div>
<style>
.progress-bar-track {
width: 100%;
height: 12px;
background: var(--color-neutral-200);
border-radius: 9999px;
overflow: hidden;
}
.progress-bar-fill {
height: 100%;
background: var(--color-primary-500);
border-radius: 9999px;
transition: width 300ms ease-out;
}
.progress-bar--indeterminate .progress-bar-fill {
width: 40%;
animation: indeterminate 1.5s ease-in-out infinite;
}
@keyframes indeterminate {
0% { transform: translateX(-100%); }
100% { transform: translateX(350%); }
}
.progress-bar-header {
display: flex;
justify-content: space-between;
margin-bottom: 4px;
font-size: 14px;
}
.progress-bar-top {
position: fixed;
top: 0; left: 0; right: 0;
height: 3px;
z-index: 9999;
background: transparent;
}
.progress-bar-top-fill {
height: 100%;
background: var(--color-primary-500);
transition: width 200ms ease-out;
}
</style>import { forwardRef } from "react";
interface ProgressBarProps {
value?: number;
max?: number;
indeterminate?: boolean;
variant?: "default" | "success" | "warning" | "danger";
size?: "xs" | "sm" | "md" | "lg";
label?: string;
showValue?: boolean;
striped?: boolean;
className?: string;
}
const sizeMap = { xs: 4, sm: 8, md: 12, lg: 20 };
const variantColors = {
default: "var(--color-primary-500)",
success: "var(--color-success-500)",
warning: "var(--color-warning-500)",
danger: "var(--color-error-500)",
};
export const ProgressBar = forwardRef<HTMLDivElement, ProgressBarProps>(
(
{
value = 0,
max = 100,
indeterminate = false,
variant = "default",
size = "md",
label,
showValue = false,
striped = false,
className,
...props
},
ref
) => {
const pct = Math.min(100, Math.max(0, (value / max) * 100));
return (
<div className={className} ref={ref}>
{(label || showValue) && (
<div style={{ display: "flex", justifyContent: "space-between", marginBottom: 4, fontSize: 14 }}>
{label && <span>{label}</span>}
{showValue && !indeterminate && <span>{Math.round(pct)}%</span>}
</div>
)}
<div
role="progressbar"
aria-valuenow={indeterminate ? undefined : value}
aria-valuemin={0}
aria-valuemax={max}
aria-label={label ?? "Progress"}
style={{
width: "100%",
height: sizeMap[size],
background: "var(--color-neutral-200)",
borderRadius: 9999,
overflow: "hidden",
}}
{...props}
>
<div
style={{
height: "100%",
width: indeterminate ? "40%" : pct + "%",
background: variantColors[variant],
borderRadius: 9999,
transition: indeterminate ? "none" : "width 300ms ease-out",
animation: indeterminate ? "indeterminate 1.5s ease-in-out infinite" : "none",
}}
/>
</div>
</div>
);
}
);
ProgressBar.displayName = "ProgressBar";Material Design 3 offers both linear and circular progress indicators with determinate and indeterminate variants. The linear variant uses a thin 4px track by default, with the indeterminate animation consisting of two bars that grow and shrink independently — a nuanced animation that feels more natural than a simple slide. Material also provides a "buffer" variant with a secondary track for pre-loading states (common in video players). The circular variant is their spinner equivalent, doubling as both progress indicator and loading spinner depending on whether you provide a value.
Ant Design provides a Progress component with line, circle, and dashboard (semicircle) types. It supports steps for segmented progress, strokeColor for custom gradients (pass an object with from and to), and a format prop for custom value display. Ant's success prop lets you define a sub-section within the bar that's colored green — useful for showing "completed" vs "in progress" portions separately.
Chakra UI's Progress component supports hasStripe and isAnimated props for the classic Bootstrap-style striped animation. It also supports the colorScheme prop to change the fill color and isIndeterminate for the looping animation. Chakra uses the native <div> approach with ARIA roles rather than the <progress> element, giving full styling control.
Bootstrap's progress bar is perhaps the most widely recognized implementation. It introduced the striped/animated pattern that many design systems have adopted. Bootstrap uses a .progress container with .progress-bar inner div, supports stacking multiple bars within one container for multi-part progress, and offers contextual classes (bg-success, bg-warning, bg-danger).
Shadcn/ui keeps it minimal: a track and fill with Tailwind classes. The component is a thin wrapper around a styled div. No built-in indeterminate, striped, or circular variants — compose those yourself.
Radix UI provides a Progress primitive with value and max props, along with ProgressIndicator as a child for the fill. It handles all ARIA attributes automatically and leaves styling entirely to you. It correctly omits aria-valuenow when value is null (indeterminate).
For choosing fill colors that maintain contrast across all variants and themes, use the Color Palette Generator. Preview animation easing with the Animation & Easing Tool.