Progress
Show how much of a known task is complete.
statusloading
Preview
Progress preview
Live · keyboard ready
When to use
- Determinate progress for uploads, imports, or long tasks.
- Progress with a visible percentage and a check at completion, via showValue.
When not to use
- Use skeleton while content is loading with no progress to report.
- Use usage-meter for quota against a limit, and gauge for dashboard metrics.
- Use stepper to show position in a multi-step flow.
shadcn CLI
Also installs (1)
- manicat-foundation
Manual install(+)
registry.jsonjson
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "progress",
"type": "registry:ui",
"title": "Progress",
"description": "Show how much of a known task is complete.",
"categories": [
"feedback"
],
"dependencies": [
"lucide-react",
"motion"
],
"registryDependencies": [
"https://manicat.viraza.net/r/manicat-foundation.json"
],
"files": [
{
"path": "registry/components/progress/progress.tsx",
"type": "registry:component",
"target": "@components/manicat/progress/progress.tsx"
},
{
"path": "registry/components/progress/progress.module.css",
"type": "registry:component",
"target": "@components/manicat/progress/progress.module.css"
}
],
"docs": "Docs and live preview: https://uiarc.dev/components/progress",
"meta": {
"tier": "free",
"kind": "component",
"docs": "https://manicat.viraza.net/components/progress",
"markdown": "https://uiarc.dev/components/progress/markdown",
"tags": [
"status",
"loading"
]
}
}Usage
Install the component, then copy this into your app. Change the content and the props to fit your screen.
example.tsxtsx
"use client";
import { useState } from "react";
import { Progress } from "@/components/manicat/progress/progress";
import { Button } from "@/components/manicat/button/button";
export function ProgressExample() {
const [value, setValue] = useState(64);
return (
<div className="grid w-full max-w-sm gap-4">
<Progress label="Migrating components" value={value} showValue />
<div className="flex gap-2">
<Button size="sm" variant="secondary" onClick={() => setValue((current) => Math.max(0, current - 20))}>
-20
</Button>
<Button size="sm" onClick={() => setValue((current) => Math.min(100, current + 20))}>
+20
</Button>
</div>
</div>
);
}
Examples
Real screens rather than isolated props. Switch to Code to copy a whole example into your project.
Upload progress A controlled progress bar driven by a timer, the way an upload or an import would drive it.
Upload progress
A controlled progress bar driven by a timer, the way an upload or an import would drive it. preview
Live · keyboard ready
upload-progress-a-controlled-progress-bar-driven-by-a-timer-the-way-an-upload-or-an-import-would-drive-it-.tsxtsx
"use client";
import { useState } from "react";
import { Button } from "@/components/manicat/button/button";
import { Progress } from "@/components/manicat/progress/progress";
export function ProgressUploadExample() {
const [value, setValue] = useState(0);
const [running, setRunning] = useState(false);
function start() {
setValue(0);
setRunning(true);
const timer = setInterval(() => {
setValue((current) => {
if (current >= 100) {
clearInterval(timer);
setRunning(false);
return 100;
}
return current + 10;
});
}, 250);
}
return (
<div className="grid w-full max-w-sm gap-4">
<Progress label="Uploading brand-assets.zip" value={value} showValue />
<Button onClick={start} disabled={running} variant="secondary">
{value === 100 ? "Upload again" : running ? "Uploading…" : "Start upload"}
</Button>
</div>
);
}
Accessibility
- Renders role="progressbar" with aria-valuemin, aria-valuemax, aria-valuenow, and a percentage aria-valuetext.
- Label changes crossfade; outgoing copies are aria-hidden while they fade.
- It is not a live region; announce completion separately if it matters.
Motion
- One smooth spring drives both the fill and the counted number, so they always agree. The fill slides in from the left to keep its rounded end.
- At 100% the fill turns to the success colour and a check settles in beside the count.
- Reduced motion jumps the fill and count to the new value.
Responsive behavior
- The bar fills its container width, so it works in cards, rows, and full-width layouts.
Performance
- One spring drives both the fill and the counted number; no observers or timers.
- It is not a live region, so announce completion separately if it matters.
Notes for AI
- Use for determinate task progress. Use skeleton while content has no progress to report, gauge or activity-rings for dashboard metrics, and usage-meter for quota against a limit.
- Pass raw value and max; the percentage is computed and clamped for you.