ProgressBar
A progress bar shows either determinate or indeterminate progress of an operation over time.
Usage
import { ProgressBar } from '@hareui/vue'<script setup lang="ts">import { ProgressBar } from '@hareui/vue'</script><template> <ProgressBar aria-label="Loading" class="w-64" label="Loading" :value="60" /></template>Anatomy
HeroUI's Label, ProgressBar.Output, ProgressBar.Track and ProgressBar.Fill parts are rendered by the component, built on Reka UI's ProgressRoot / ProgressIndicator.
<ProgressBar :value="60" label="Loading">
<template #label /> <!-- optional: replaces the label text -->
<template #output="{ percentage, valueText, indeterminate }" /> <!-- optional: replaces the value text -->
</ProgressBar>- The label renders when the
labelprop or#labelslot is provided, and labels the bar viaaria-labelledby. - The output renders next to a label unless the bar is indeterminate; set
show-outputto force it on or off.
ui keys: base, label, output, track, fill.
Examples
Sizes
<script setup lang="ts">import { ProgressBar } from '@hareui/vue'</script><template> <div class="flex w-64 flex-col gap-6"> <ProgressBar aria-label="Small" label="Small" size="sm" :value="40" /> <ProgressBar aria-label="Medium" label="Medium" size="md" :value="60" /> <ProgressBar aria-label="Large" label="Large" size="lg" :value="80" /> </div></template>Colors
<script setup lang="ts">import { ProgressBar } from '@hareui/vue'</script><template> <div class="flex w-64 flex-col gap-6"> <ProgressBar aria-label="Default" color="default" label="Default" :value="50" /> <ProgressBar aria-label="Accent" color="accent" label="Accent" :value="50" /> <ProgressBar aria-label="Success" color="success" label="Success" :value="50" /> <ProgressBar aria-label="Warning" color="warning" label="Warning" :value="50" /> <ProgressBar aria-label="Danger" color="danger" label="Danger" :value="50" /> </div></template>Without Label
When no visible label is needed, use aria-label for accessibility.
<script setup lang="ts">import { ProgressBar } from '@hareui/vue'</script><template> <ProgressBar aria-label="Loading progress" class="w-64" :value="45" /></template>Indeterminate
Use indeterminate when progress cannot be determined.
<script setup lang="ts">import { ProgressBar } from '@hareui/vue'</script><template> <ProgressBar aria-label="Loading" class="w-64" indeterminate label="Loading..." /></template>Custom Value Scale
Use min-value, max-value, and format-options to customize the value range and display format.
<script setup lang="ts">import type { ListBoxItem } from '@hareui/vue'import { Label, NumberField, ProgressBar, Select, Separator } from '@hareui/vue'import { ref } from 'vue'const formatStyleOptions: ListBoxItem[] = [ { label: 'Currency', value: 'currency' }, { label: 'Percent', value: 'percent' }, { label: 'Decimal', value: 'decimal' }, { label: 'Unit', value: 'unit' },]const formatOptionsMap: Record<string, Intl.NumberFormatOptions> = { currency: { currency: 'USD', style: 'currency' }, decimal: { style: 'decimal' }, percent: { style: 'percent' }, unit: { style: 'unit', unit: 'mile' },}const value = ref(750)const minValue = ref(0)const maxValue = ref(1000)const format = ref('percent')function setMinValue(v: number | null | undefined) { minValue.value = v ?? 0 if (value.value < minValue.value) value.value = minValue.value}function setMaxValue(v: number | null | undefined) { maxValue.value = v ?? minValue.value + 1 if (value.value > maxValue.value) value.value = maxValue.value}</script><template> <div class="flex w-full flex-col gap-6 md:flex-row md:items-center md:gap-10"> <div class="flex w-full max-w-md flex-1 justify-center"> <ProgressBar aria-label="Revenue" class="w-full max-w-52" :format-options="formatOptionsMap[format]" label="Progress" :max-value="maxValue" :min-value="minValue" :value="value" /> </div> <Separator class="md:hidden" /> <Separator class="hidden md:block" orientation="vertical" /> <div class="flex max-w-52 flex-col gap-3"> <Label class="text-xs font-medium text-muted">Options</Label> <NumberField v-model="value" label="Value" :max="maxValue" :min="minValue" variant="secondary" /> <NumberField label="Min Value" :max="maxValue - 1" :min="0" :model-value="minValue" variant="secondary" @update:model-value="setMinValue" /> <NumberField label="Max Value" :max="2000" :min="minValue + 1" :model-value="maxValue" variant="secondary" @update:model-value="setMaxValue" /> <Select v-model="format" :items="formatStyleOptions" label="Format" variant="secondary" /> </div> </div></template>Customization
Tailwind CSS
Use the class prop for the root and the ui prop for the label, output, track and fill:
<script setup lang="ts">import { ProgressBar } from '@hareui/vue'</script><template> <ProgressBar aria-label="Upload progress" class="w-64" label="Uploading resume.pdf" :ui="{ label: 'font-medium text-foreground', output: 'text-xs text-muted tabular-nums', track: 'rounded-full bg-default', fill: 'rounded-full bg-accent', }" :value="45" /></template>Global theme override
Override the progressBar theme app-wide with createHareUI:
app.use(createHareUI({
ui: {
progressBar: {
slots: {
base: 'w-full gap-2',
track: 'h-3 rounded-full',
fill: 'rounded-full',
},
},
},
}))Styling Reference
Each rendered element carries the same data-slot string as HeroUI, so selectors translate 1:1.
ui key | data-slot | Description |
|---|---|---|
base | progress-bar | Base container (grid layout) |
label | label | Label text (Label theme plus the grid placement) |
output | progress-bar-output | Value text display |
track | progress-bar-track | Track background |
fill | progress-bar-fill | Filled portion of the track |
HeroUI's modifier classes correspond to props (HareUI renders utilities, not BEM classes, so style through class, ui or createHareUI):
size:sm(thinner track),md(default),lg(thicker track) (.progress-bar--{size})color:default,accent(default),success,warning,danger(.progress-bar--{color})
API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | 0 | The current value (clamped to minValue–maxValue). |
minValue | number | 0 | The minimum value. |
maxValue | number | 100 | The maximum value. |
indeterminate | boolean | false | Whether progress is indeterminate (no value; the fill loops). |
formatOptions | Intl.NumberFormatOptions | { style: 'percent' } | Number format for the value display. Percent formats the fraction of the range. |
valueLabel | string | - | Custom value text (e.g. '3 of 4'), replacing the formatted value in the output and aria-valuetext. |
label | string | - | Visible label, rendered in the top-left grid area and referenced by aria-labelledby. |
showOutput | boolean | undefined | Whether to render the value output. Defaults to showing it next to a label, unless indeterminate. |
size | "md" | "sm" | "lg" | 'md' | Size of the progress track. |
color | "default" | "accent" | "success" | "warning" | "danger" | 'accent' | Color of the fill bar. |
ui | ComponentSlots<{ slots: { base: string[]; label: string; output: string; track: string; fill: string; }; variants: { color: { default: { base: string; }; accent: { base: string; }; success: { base: string; }; warning: { base: string; }; danger: { base: string; }; }; size: { sm: { track: string; fill: string; }; md: {}; lg: { track: string; fill: string; }; }; indeterminate: { true: { fill: string; }; }; }; defaultVariants: { color: string; size: string; }; }> | - | Per-slot class overrides. |
Slots
| Slot | Props | Description |
|---|---|---|
label | ProgressBarSlotProps | Replaces the label text. |
output | ProgressBarSlotProps | Replaces the value output text. |
#label and #output receive:
| Prop | Type | Description |
|---|---|---|
percentage | number | undefined | The percentage of the progress (0-100) |
valueText | string | undefined | The formatted value text |
indeterminate | boolean | Whether progress is indeterminate |