Progress
Overview
A progress indicator for determinate or indeterminate task completion. SProgress combines the Aria ProgressRoot/ProgressIndicator primitives with the progressVariants style recipe, and SProgressCircle offers a circular variant via progressCircleVariants. Both share an imperative API (progress.start()/done()) surfaced through SProgressProvider.
Use it for uploads, downloads, multi-step flows, or top-of-page loading bars. Prefer spinner for an indefinite wait, and skeleton for reserving content space before it loads.
Usage
Features
- 🧩 Aria/styled split —
ProgressRoot/ProgressIndicatorown state,role="progressbar"ARIA and derived values;SProgressinjectsprogressVariants - 🔢 Determinate / indeterminate —
modelValueshows a concrete value; omitting it shows an indeterminate bar - 🎨 8 colors —
ThemeColorvalues on the indicator - 📐 6 sizes — xs–2xl
size - ⭕ Circle variant —
SProgressCirclefor a circular gauge with configurablestrokeWidth - 🏷️ Custom label —
getValueLabel/getValueTextshape thearia-label/aria-valuetext - ⚡ Imperative API —
progress.start()/set()/inc()/done()etc. viaSProgressProviderfor top-of-page loading - ♿ Accessible —
role="progressbar"witharia-valuemin/max/nowand a localizedaria-labelfallback
Component family
SProgress(styled) — the linear wrapper;progressVariantsrecipeSProgressCircle(styled) — the circular wrapper;progressCircleVariantsrecipeSProgressProvider(styled) — mounts the imperative progress layerProgressRoot(Aria) — the state owner; normalizesmodelValue/max, derives state/value percent, rendersrole="progressbar"ProgressIndicator(Aria) — the fill; sizes via CSS vars/transform perdirProgressCircleCompact/ProgressCompact(Aria) — the aggregated compositesprogress(imperative) — the sharedstart/set/inc/done/configurecontroller
Demos
Basic
Slot
Circle
Provider
Circle Progress
<script setup lang="ts">
import { SProgressCircle } from '@vean/ui';
</script>
<template>
<SProgressCircle :model-value="72" size="xl">
<template #default="{ valuePercent }">{{ Math.round(valuePercent ?? 0) }}%</template>
</SProgressCircle>
</template>SProgressCircle supports the same props, emits, slot props, and Ui type as SProgress, and adds the following prop:
| Prop | Type | Default | Description |
|---|---|---|---|
strokeWidth | number | 8 | Stroke width of the circular indicator. |
Progress Provider
Mount SProgressProvider once near your app root before calling the imperative progress(...) API. SConfigProvider mounts it automatically, so most applications can call progress directly.
<script setup lang="ts">
import { SButton, SProgressProvider, progress } from '@vean/ui';
const handleClick = () => {
progress.start();
window.setTimeout(() => {
progress.done();
}, 1200);
};
</script>
<template>
<SProgressProvider />
<SButton @click="handleClick">Start loading</SButton>
</template>progress Methods
| Method | Description |
|---|---|
start() | Show the progress bar with the default start position and delay, then start trickling. |
set(value) | Set the raw progress value between minimum and maximum. |
inc() | Increment the current progress value. |
dec() | Decrement the current progress value. |
trickle() | Apply one automatic increment step. |
done() | Complete the progress bar and hide it after the configured delay. |
configure() | Update the shared progress options. |
pause() | Pause automatic trickling. |
resume() | Resume automatic trickling. |
remove() | Hide the progress bar immediately. |
reset() | Reset the shared progress state and options. |
isStarted() | Check whether the progress flow is active. |
isRendered() | Check whether a progress provider is currently mounted. |
promise() | Bind the progress lifecycle to a promise or promise factory. |
API
Aria Layer
8Progress
Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | ClassValue | — | Additional class names applied to the root element. |
color | ThemeColor | — | Theme color of the component. |
size | ThemeSize | — | Visual size of the component. |
ui | Partial<ProgressUi> | — | Per-slot class overrides for the component. |
indicatorProps | ProgressIndicatorProps | — | Properties forwarded to the indicator element. |
dir | Direction | — | Reading direction of the component. |
modelValue | number | null | — | The controlled progress value. Can be bind as `v-model`. |
max | number | — | The maximum progress value. |
getValueLabel | ((value: number | null, max: number) => string) | — | The accessible label of the current progress value. |
getValueText | ((value: number | null, max: number) => string) | — | The accessible text of the current progress value. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:modelValue | number | null | undefined | Emitted when the model value changes. |
update:max | number | Emitted when the max value changes. |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
default | ProgressSlotProps | Custom content for the default slot. |
ProgressCircle
Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | ClassValue | — | Additional class names applied to the root element. |
color | ThemeColor | — | Theme color of the component. |
size | ThemeSize | — | Visual size of the component. |
ui | Partial<ProgressUi> | — | Per-slot class overrides for the component. |
strokeWidth | number | — | Stroke width. |
circleSvgProps | ProgressCircleSvgProps | — | Properties forwarded to the ProgressCircleSvg component. |
dir | Direction | — | Reading direction of the component. |
modelValue | number | null | — | The controlled progress value. Can be bind as `v-model`. |
max | number | — | The maximum progress value. |
getValueLabel | ((value: number | null, max: number) => string) | — | The accessible label of the current progress value. |
getValueText | ((value: number | null, max: number) => string) | — | The accessible text of the current progress value. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:modelValue | number | null | undefined | Emitted when the model value changes. |
update:max | number | Emitted when the max value changes. |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
default | ProgressSlotProps | Custom content for the default slot. |
ProgressCircleCompact
Props
| Prop | Type | Default | Description |
|---|---|---|---|
strokeWidth | number | — | Stroke width. |
circleSvgProps | ProgressCircleSvgProps | — | Properties forwarded to the ProgressCircleSvg component. |
dir | Direction | — | Reading direction of the component. |
modelValue | number | null | — | The controlled progress value. Can be bind as `v-model`. |
max | number | — | The maximum progress value. |
getValueLabel | ((value: number | null, max: number) => string) | — | The accessible label of the current progress value. |
getValueText | ((value: number | null, max: number) => string) | — | The accessible text of the current progress value. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:modelValue | number | null | undefined | Emitted when the model value changes. |
update:max | number | Emitted when the max value changes. |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
default | ProgressSlotProps | Custom content for the default slot. |
ProgressCircleSvg
Props
| Prop | Type | Default | Description |
|---|---|---|---|
strokeWidth | number | — | Stroke width. |
ProgressCompact
Props
| Prop | Type | Default | Description |
|---|---|---|---|
indicatorProps | ProgressIndicatorProps | — | Properties forwarded to the indicator element. |
dir | Direction | — | Reading direction of the component. |
modelValue | number | null | — | The controlled progress value. Can be bind as `v-model`. |
max | number | — | The maximum progress value. |
getValueLabel | ((value: number | null, max: number) => string) | — | The accessible label of the current progress value. |
getValueText | ((value: number | null, max: number) => string) | — | The accessible text of the current progress value. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:modelValue | number | null | undefined | Emitted when the model value changes. |
update:max | number | Emitted when the max value changes. |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
default | ProgressSlotProps | Custom content for the default slot. |
ProgressIndicator
Props
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
ProgressProvider
Props
| Prop | Type | Default | Description |
|---|---|---|---|
direction | Direction | — | Direction. |
minimum | number | — | Minimum. |
maximum | number | — | Maximum. |
easing | string | — | Easing. |
speed | number | — | Speed. |
trickle | boolean | — | Whether trickle. |
trickleSpeed | number | — | Trickle speed. |
ProgressRoot
Props
| Prop | Type | Default | Description |
|---|---|---|---|
dir | Direction | — | Reading direction of the component. |
modelValue | number | null | — | The controlled progress value. Can be bind as `v-model`. |
max | number | — | The maximum progress value. |
getValueLabel | ((value: number | null, max: number) => string) | — | The accessible label of the current progress value. |
getValueText | ((value: number | null, max: number) => string) | — | The accessible text of the current progress value. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:modelValue | number | null | undefined | Emitted when the model value changes. |
update:max | number | Emitted when the max value changes. |
Notes
Architecture and benchmark differences
ProgressRoot owns the value normalization (getValidMax/getValidModelValue), state derivation (indeterminate/loading/complete) and the role="progressbar" ARIA contract, while the primitives stay style-free and only the UI wrapper injects the recipe classes. This mirrors shadcn/ui’s headless/styled split, unlike Ant Design, Element Plus, Mantine and Naive UI which ship a single config-driven progress. Vean’s distinguishing feature is the nprogress-style imperative controller (progress.start()/done()) and the SProgressCircle gauge, both of which the single-package libraries handle as separate components or omit.
| Capability | VeanUI | shadcn/ui | Ant Design Progress | Element Plus Progress | Mantine Progress | Naive UI Progress |
|---|---|---|---|---|---|---|
| Linear progress | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Circle progress | ✅ | — | ✅ | ✅ | ✅ | ✅ |
| Indeterminate | ✅ | — | ✅ | ✅ | ✅ | ✅ |
Imperative API (start/done) | ✅ | — | — | — | — | — |
| Color variants (8) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Size variants (6) | ✅ | — | — | — | — | — |
role="progressbar" ARIA | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Custom value label | ✅ | — | — | ✅ | ✅ | ✅ |
— = unsupported or a different interaction model.
Cautions
- For a determinate value the
aria-labelfalls back to the localized percentage label (45%); the localizedprogress.ariaLabelonly applies to indeterminate progress. Set an explicitaria-labelto override either. - Indeterminate progress (no
modelValue) has noaria-valuenow; itsdata-stateisindeterminate. - The imperative
progressAPI requires a mountedSProgressProvider—SConfigProvidermounts one automatically. SProgressCirclestroke width is clamped between 1 andviewbox/4; passstrokeWidthfor a thinner/thicker gauge.- The indicator uses
transform+ CSS vars and swaps direction under RTL viadir.
Roadmap
No blocking gaps identified for the core progress API.
FAQ
How do I show a determinate value?
Pass model-value:
<SProgress :model-value="45" />How do I show an indeterminate bar?
Omit model-value:
<SProgress />How do I use the top-of-page loading bar?
Mount SProgressProvider (or rely on SConfigProvider) and call the imperative API:
<script setup>
const load = () => {
progress.start();
setTimeout(() => progress.done(), 1200);
};
</script>
<template><SButton @click="load">Load</SButton></template>How do I customize the announced label?
Pass get-value-label/get-value-text or an explicit aria-label:
<SProgress :model-value="45" aria-label="Upload progress" />Circle or linear?
Use SProgressCircle for a compact gauge in a dashboard/stat, and SProgress for a full-width linear indicator or the top-of-page bar.