A Radix progress bar with clamped values, optional percentage text, and a reduced-motion-safe pending state.
Organizing 48 documents into your workspace
import { Progress } from "@/components/ballmac/progress"
export default function ProgressDemo() {
return (
<div className="w-full max-w-sm rounded-xl border bg-card p-5 shadow-sm">
<div className="flex justify-between gap-3 text-sm">
<span className="font-semibold">Importing your files</span>
<span className="text-muted-foreground">Step 3 of 4</span>
</div>
<p className="mt-1 mb-5 text-xs text-muted-foreground">
Organizing 48 documents into your workspace
</p>
<Progress value={68} showValue aria-label="Import progress" />
</div>
)
}Installation
$ pnpm dlx shadcn@latest add @ballmac/progressInstall the dependencies.
$ pnpm add radix-uiCopy the source into your project.
components/ballmac/progress.tsx// Ballmac UI: Progress. https://ui.ballmac.com/components/progress // Based on shadcn/ui Progress (MIT, Copyright (c) 2023 shadcn), adding a labeled value and reduced-motion-safe indeterminate state. import * as React from "react" import { Progress as ProgressPrimitive } from "radix-ui" import { cn } from "@/lib/utils" type ProgressProps = React.ComponentProps<typeof ProgressPrimitive.Root> & { /** Show the value in a label below the bar. */ showValue?: boolean } function Progress({ className, value, max = 100, showValue = false, ...props }: ProgressProps) { const safeMax = Number.isFinite(max) && max > 0 ? max : 100 const safeValue = value == null ? null : Math.max(0, Math.min(safeMax, value)) const percent = safeValue == null ? null : Math.round((safeValue / safeMax) * 100) return ( <div data-slot="progress-group" className={cn("w-full", className)}> <ProgressPrimitive.Root data-slot="progress" value={safeValue} max={safeMax} className="bg-muted relative h-2 w-full overflow-hidden rounded-full" {...props} > <ProgressPrimitive.Indicator data-slot="progress-indicator" className={cn( "bg-primary block h-full rounded-full transition-[width] duration-200 motion-reduce:transition-none", percent == null && "w-1/3 motion-safe:animate-pulse motion-reduce:w-full", )} style={percent == null ? undefined : { width: `${percent}%` }} /> </ProgressPrimitive.Root> {showValue && ( <span data-slot="progress-value" className="text-muted-foreground mt-2 block text-end text-xs tabular-nums" > {percent == null ? "In progress" : `${percent}%`} </span> )} </div> ) } export { Progress, type ProgressProps }Update the import paths to match your project setup.
Usage
import { Progress } from "@/components/ballmac/progress"The full example is in the Code tab above.
Examples
States and variants
import { Progress } from "@/components/ballmac/progress"
export default function ProgressStates() {
return (
<div className="flex w-full max-w-sm flex-col gap-4">
<div className="text-sm font-medium">Almost there</div>
<Progress value={91} showValue aria-label="Upload progress" />
<div className="text-sm font-medium">Waiting for a response</div>
<Progress value={null} showValue aria-label="Connection progress" />
</div>
)
}API reference
| Prop | Type | Default |
|---|---|---|
showValueShow the value in a label below the bar. | boolean | false |
Also accepts the standard attributes of its root element.
Use with AI
A Radix progress bar with clamped values, optional percentage text, and a reduced-motion-safe pending state. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Progress (@ballmac/progress) to this project with the shadcn MCP, then use it where it fits.
Use it for
- Show completion of a bounded task
- Show work in progress when completion is unknown
Not for
- Use a spinner for very short waits
Registry JSON: https://ui.ballmac.com/r/progress.json
Credits
Based on shadcn/ui Progress, adapted by Ballmac. Free to use in personal and commercial projects.
- npm
- radix-ui
- Registry
- shadcn/utils
Pairs well with
Spinner
A compact loading indicator with three sizes, an accessible label, and reduced-motion behavior.
Alert Dialog
Focus-managed confirmation for consequential actions, with a clear cancel path, optional media, and a token-based destructive action.
Alert
A semantic callout with five theme-aware tones, clear icon placement, an action row, and opt-in urgent announcements.
Banner
A page-wide announcement with semantic tones, optional action, and controlled or local dismissal.