Elements

Progress

Show a horizontal bar to indicate task progression.

Usage

Pass an integer as the value from 0 to 100 to the Progress bar component.

70%
<UProgress :value="70" />
Check out the Range component for forms.

Max

You may also set the max number to set the maximum progress value, which will be relative to 100% percent.

40%
<UProgress :value="2" :max="5" />

Steps

You can set an array of named steps in the max prop to show the active step, at the same time it sets the maximum value.

The first step is always shown at 0%, making the last 100%.

0%
Waiting to start
Cloning...
Migrating...
Deployed!
<UProgress
  :value="0"
  :max="['Waiting to start', 'Cloning...', 'Migrating...', 'Deployed!']"
/>

Progress indicator

You can add a numeric indicator, which will show the percent on top the progress track.

20%
20%
<UProgress :value="20" indicator />

Indeterminate

By not setting a value, or setting it as null, the progress bar becomes indeterminate. The bar will be animated as a carousel, but you can change it using the animation prop for an inverse carousel, a swinging bar or an elastic bar.

NaN%
<UProgress animation="carousel" />

Size

Use the size prop to change the size of the progress bar.

70%
<UProgress size="md" :indicator="false" :value="70" />

Style

Use the color prop to change the visual style of the Progress bar. The color can be any color from the ui.colors object.

70%
<UProgress color="primary" :indicator="false" :value="70" />

Slots

indicator

You can use the #indicator slot to show a custom indicator above the progress bar. It receives the current percent of progress.

🔥 Too hot!
88%
<script setup>
const temp = ref(35)

const color = computed(() => {
  switch (true) {
  case temp.value < 10: return 'blue'
  case temp.value < 20: return 'amber'
  case temp.value < 30: return 'orange'
  default: return 'red'
  }
})
</script>

<template>
  <UProgress :value="temp" :max="40" :color="color">
    <template #indicator="{ percent }">
      <div class="text-right" :style="{ width: `${percent}%` }">
        <span v-if="temp < 10" class="text-blue-500">Too cold!</span>
        <span v-else-if="temp < 20" class="text-amber-500">Warm</span>
        <span v-else-if="temp < 30" class="text-orange-500">Hot</span>
        <span v-else class="text-red-500 font-bold">🔥 Too hot!</span>
      </div>
    </template>
  </UProgress>
</template>

step-<index>

Use the #step-<index> to alter the HTML being shown for each step.

50%
Cloning...
Migrating...
Deploying...
<script setup>
const task = ref(1)

const steps = [
  'Cloning...',
  'Migrating...',
  'Deploying...'
]
</script>

<template>
  <UProgress :value="task" :max="steps" indicator>
    <template #step-0="{ step }">
      <span class="text-lime-500">
        <UIcon name="i-heroicons-arrow-down-circle" /> {{ step }}
      </span>
    </template>

    <template #step-1="{ step }">
      <span class="text-amber-500">
        <UIcon name="i-heroicons-circle-stack" /> {{ step }}
      </span>
    </template>

    <template #step-2="{ step }">
      <span class="text-blue-500">
        <UIcon name="i-heroicons-hand-thumb-up" /> {{ step }}
      </span>
    </template>
  </UProgress>
</template>

Props

ui
{}
{}
size
"sm" | "2xs" | "xs" | "md" | "lg" | "xl" | "2xl"
config.default.size
value
number
null
color
string
config.default.color
max
number | any[]
100
animation
"carousel" | "carousel-inverse" | "swing" | "elastic"
config.default.animation
indicator
boolean
false

Config

{
  "wrapper": "w-full flex flex-col gap-2",
  "indicator": {
    "container": {
      "base": "flex flex-row justify-end",
      "width": "min-w-fit",
      "transition": "transition-all"
    },
    "align": "text-end",
    "width": "w-fit",
    "color": "text-gray-400 dark:text-gray-500",
    "size": {
      "2xs": "text-xs",
      "xs": "text-xs",
      "sm": "text-sm",
      "md": "text-sm",
      "lg": "text-sm",
      "xl": "text-base",
      "2xl": "text-base"
    }
  },
  "progress": {
    "base": "block appearance-none border-none overflow-hidden",
    "width": "w-full [&::-webkit-progress-bar]:w-full",
    "size": {
      "2xs": "h-px",
      "xs": "h-0.5",
      "sm": "h-1",
      "md": "h-2",
      "lg": "h-3",
      "xl": "h-4",
      "2xl": "h-5"
    },
    "rounded": "rounded-full [&::-webkit-progress-bar]:rounded-full",
    "track": "[&::-webkit-progress-bar]:bg-gray-200 [&::-webkit-progress-bar]:dark:bg-gray-700 [@supports(selector(&::-moz-progress-bar))]:bg-gray-200 [@supports(selector(&::-moz-progress-bar))]:dark:bg-gray-700",
    "bar": "[&::-webkit-progress-value]:rounded-full [&::-webkit-progress-value]:transition-all [&::-webkit-progress-value]:ease-in-out [&::-moz-progress-bar]:rounded-full",
    "color": "text-{color}-500 dark:text-{color}-400",
    "background": "[&::-webkit-progress-value]:bg-current [&::-moz-progress-bar]:bg-current",
    "indeterminate": {
      "base": "indeterminate:relative",
      "rounded": "indeterminate:after:rounded-full [&:indeterminate::-webkit-progress-value]:rounded-full [&:indeterminate::-moz-progress-bar]:rounded-full"
    }
  },
  "steps": {
    "base": "grid grid-cols-1",
    "color": "text-{color}-500 dark:text-{color}-400",
    "size": {
      "2xs": "text-xs",
      "xs": "text-xs",
      "sm": "text-sm",
      "md": "text-sm",
      "lg": "text-sm",
      "xl": "text-base",
      "2xl": "text-base"
    }
  },
  "step": {
    "base": "transition-all opacity-0 truncate row-start-1 col-start-1",
    "align": "text-end",
    "active": "opacity-100",
    "first": "text-gray-500 dark:text-gray-400"
  },
  "animation": {
    "carousel": "bar-animation-carousel",
    "carousel-inverse": "bar-animation-carousel-inverse",
    "swing": "bar-animation-swing",
    "elastic": "bar-animation-elastic"
  },
  "default": {
    "color": "primary",
    "size": "md",
    "animation": "carousel"
  }
}