docs / core / progress
Progress
a bar that fills to show how far along something is, or shimmers when the end is unknown. it renders no text, so it always needs a label.
intensity · recedesrsc · server
provenance
authored in usvalayer
core / primitivesintensity
recedes · safe anywhere, including dense surfacescomposition
uploads, installs, quota rows inside Card or a table cellpair with a text line that says what is progressingnot a meter for static values. it implies something is underwaynot a page loader. indeterminate work that owns the view gets Spinnera11y
role="progressbar" with aria-valuenow/min/max, valuenow omitted when indeterminate · bring an aria-label · motion stops under reduced motionjest-axedependencies
class-variance-authoritylive demo · try it out
customize
value0 to 100; ignored when indeterminate
sizetrack height
glowaccent halo on the fill
indeterminatedrop the value for the shimmer
usage
import { Progress } from "usva/primitives/progress";
<Progress value={55} aria-label="Upload" />props
| prop | type | default | notes |
|---|---|---|---|
| value | number | — | current progress, clamped to [0, max]. omit it for the indeterminate shimmer. |
| max | number | 100 | upper bound for value. |
| size | "sm" | "md" | "lg" | "md" | track height, 4 to 12 pixels. pick by the density of the row it sits in. |
| glow | boolean | false | the accent halo on the fill. one glowing bar per view. |
get it
npx shadcn add https://usva.build/r/progress.jsonsource · components/ui/progress.tsxexactly what this command copies
import { cva, type VariantProps } from "class-variance-authority";
import * as React from "react";
import { cn } from "@/lib/utils";
export const progressVariants = cva(
"w-full overflow-hidden rounded-full bg-border-strong",
{
variants: {
size: {
sm: "h-1",
md: "h-2",
lg: "h-3",
},
},
defaultVariants: { size: "md" },
},
);
export interface ProgressProps
extends Omit<React.HTMLAttributes<HTMLDivElement>, "children">,
VariantProps<typeof progressVariants> {
value?: number;
max?: number;
glow?: boolean;
}
export const Progress = React.forwardRef<HTMLDivElement, ProgressProps>(
({ className, size, value, max = 100, glow = false, ...props }, ref) => {
const indeterminate = value === undefined;
const pct = indeterminate
? 0
: Math.max(0, Math.min(100, (value / max) * 100));
const complete = !indeterminate && pct >= 100;
return (
<div
ref={ref}
role="progressbar"
aria-valuemin={0}
aria-valuemax={max}
aria-valuenow={indeterminate ? undefined : value}
className={cn(progressVariants({ size }), className)}
{...props}
>
<div
className={cn(
"h-full rounded-full",
complete ? "bg-live" : "bg-accent bg-gradient-accent",
glow && "glow-accent",
indeterminate
? "w-full animate-shimmer motion-reduce:animate-none"
: "transition-layout duration-ambient ease-soft motion-reduce:transition-none",
)}
style={indeterminate ? undefined : { width: `${pct}%` }}
/>
</div>
);
},
);
Progress.displayName = "Progress";