Progress
Progress indicators as a bar (Progress) and a circle (ProgressCircle) — determinate and indeterminate, 4 colors, 3 sizes.
'use client';
import { Progress } from '@akhatmirzo/ui';
export default function ProgressBasic() {
return (
<div className="flex w-full max-w-md flex-col gap-6">
<Progress value={64} label="Yuklanmoqda" showValue />
<Progress
value={3}
max={5}
label="Profil to'ldirildi"
showValue
formatValue={(value, max) => `${value} / ${max}`}
/>
</div>
);
}
Import
import { Progress, ProgressCircle } from '@akhatmirzo/ui';When to use
- For tasks with a known length — file uploads, exports, profile completion.
- When you can't tell how long it takes, pass
value={null}(indeterminate). - For short waits (under a second) you don't need a progress bar — a
Spinnerinside the button is enough. - Use the circle in cards, next to avatars or where space is tight; use the bar in lists and forms.
- Show the number too (
showValue) so people see the exact value.
Indeterminate
When value is missing or null, a segment slides across the bar and the circle spins. With
reduced motion enabled (prefers-reduced-motion), only a soft pulse remains.
'use client';
import { Progress, ProgressCircle } from '@akhatmirzo/ui';
export default function ProgressIndeterminate() {
return (
<div className="flex w-full max-w-md flex-col gap-6">
<Progress value={null} label="Fayl tayyorlanmoqda" />
<ProgressCircle value={null} label="Yuklanmoqda" />
</div>
);
}
Colors and sizes
color: primary (default), success, warning and danger. size: sm is a 4px bar, md
8px and lg 12px.
'use client';
import { Progress } from '@akhatmirzo/ui';
export default function ProgressColorsSizes() {
return (
<div className="flex w-full max-w-md flex-col gap-5">
<Progress value={40} size="sm" aria-label="Kichik chiziq" />
<Progress value={55} color="success" aria-label="O'rta chiziq" />
<Progress value={80} size="lg" color="warning" aria-label="Katta chiziq" />
<Progress value={95} color="danger" label="Disk to'lib bormoqda" showValue />
</div>
);
}
ProgressCircle
Circle sizes: sm is 24px, md 40px and lg 64px. showValue puts the value in the middle
(not on sm — there's no room). On the circle, label isn't visible; it becomes the accessible
name.
'use client';
import { ProgressCircle } from '@akhatmirzo/ui';
export default function ProgressCircleDemo() {
return (
<div className="flex flex-wrap items-center gap-6">
<ProgressCircle value={30} size="sm" label="Sinxronlash" />
<ProgressCircle value={58} showValue label="Saqlash" />
<ProgressCircle value={72} size="lg" showValue label="Kurs" color="success" />
<ProgressCircle value={100} size="lg" showValue label="Tayyor" color="success" />
</div>
);
}
Live example
When the value changes, the bar moves smoothly over 300ms. You can switch the color to success
once it's done.
'use client';
import { Button, Progress } from '@akhatmirzo/ui';
import { useEffect, useState } from 'react';
export default function ProgressUpload() {
const [value, setValue] = useState(0);
const [running, setRunning] = useState(false);
useEffect(() => {
if (!running) return;
const id = setInterval(() => setValue((v) => Math.min(v + 8, 100)), 300);
return () => clearInterval(id);
}, [running]);
const done = value === 100;
if (done && running) setRunning(false);
return (
<div className="flex w-full max-w-md flex-col gap-4">
<Progress value={value} label="hisobot.pdf" showValue color={done ? 'success' : 'primary'} />
<Button
size="sm"
className="self-start"
disabled={running}
onClick={() => {
setValue(0);
setRunning(true);
}}
>
{done ? 'Qayta yuklash' : 'Yuklashni boshlash'}
</Button>
</div>
);
}
Props
Progress and ProgressCircle share the same props and pass every other <div> attribute to the
root (except color).
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | null | null | Current value (clamped to 0…max); null means indeterminate |
max | number | 100 | Maximum value |
color | 'primary' | 'success' | 'warning' | 'danger' | 'primary' | Fill color |
size | 'sm' | 'md' | 'lg' | 'md' | Bar: 4 / 8 / 12px; circle: 24 / 40 / 64px |
label | ReactNode | — | Bar: shown above on the left; circle: hidden accessible name (aria-label) |
showValue | boolean | false | Value text: right of the bar, center of the circle (not on sm circles) |
formatValue | (value: number, max: number) => string | "64%" | Value text, both visible and for aria-valuetext |
Data attributes
On the root: data-state (indeterminate · loading · complete), data-color and
data-size.
Accessibility
role="progressbar"witharia-valuemin,aria-valuemax,aria-valuenowandaria-valuetext(theformatValueresult) set for you.- When indeterminate,
aria-valuenowandaria-valuetextare omitted, so screen readers treat it as busy. - A name is required: pass
label, or setaria-label/aria-labelledbyyourself. On the barlabelis linked witharia-labelledby; on the circle a textlabelbecomesaria-label. - The visible value is
aria-hidden— it's already read viaaria-valuetext. - With reduced motion, sliding and spinning stop and only a pulse remains.