---
name: progress
title: Progress
url: https://ui.frontic.com/components/ui/progress.md
tier: ui
install: npx @frontic/ui add progress
files:
  - components/ui/progress/Progress.vue
  - components/ui/progress/index.ts
dependencies:
  - "@vueuse/core"
  - reka-ui
---

# Progress

Shows how far a task has progressed, as a share of the whole; slides a segment while that is unknown.

## Install

```sh
npx @frontic/ui add progress
```

Used by: [Review Cards](https://ui.frontic.com/components/sections/review-cards.md), [Reviews](https://ui.frontic.com/components/sections/reviews.md), [Cart Compact](https://ui.frontic.com/components/templates/cart-compact.md)

npm packages: `@vueuse/core`, `reka-ui`

## Examples

```vue
<script setup lang="ts">
import { Progress } from '@/components/ui/progress'
</script>

<template>
  <div class="flex w-xs max-w-full flex-col gap-8 text-sm">
    <div class="flex flex-col gap-2">
      <div class="flex justify-between gap-4">
        <span>Free shipping</span>
        <span class="text-muted-foreground">€30 to go</span>
      </div>
      <Progress :model-value="70" aria-label="Free shipping" />
    </div>
    <div class="flex flex-col gap-2">
      <span>Checking stock at Basecamp Berlin…</span>
      <Progress :model-value="null" aria-label="Checking stock" />
    </div>
  </div>
</template>
```

## API reference

### Progress

Shows how far a task has progressed, as a share of the whole.

`data-slot="progress"` · `Progress.vue`

Styling: `progress`, which extends [`track`](https://ui.frontic.com/DESIGN.md#track); `progress-indicator`, which extends [`track-fill`](https://ui.frontic.com/DESIGN.md#track-fill). Change them there and every part that extends them follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` |  | The element or component this component should render as. Can be overwritten by `asChild`. |
| `max` | `number` | `100` | The maximum progress value. |
| `getValueLabel` | `((value: number \| null \| undefined, max: number) => string \| undefined)`: `(value: number \| null \| undefined, max: number): string` |  | A function to get the accessible label text in a human-readable format.<br><br> If not provided, the value label will be read as the numeric value as a percentage of the max value. |
| `getValueText` | `((value: number \| null \| undefined, max: number) => string \| undefined)`: `(value: number \| null \| undefined, max: number): string` |  | A function to get the accessible value text representing the current value in a human-readable format. |
| `modelValue` | `number \| null`: `null \| number` | `0` | How far the task is, from 0 to `max`; `null` while that is unknown, which slides a segment along the track. Can be bound with `v-model`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-max` |  | Carries the `max` prop. |
| `data-state` | `'indeterminate' \| 'loading' \| 'complete'` | The state the part is in, one value per state. |
| `data-value` |  | The current progress; absent while indeterminate. |

## Styling

| Part | Recipe |
| --- | --- |
| `default` | [`progress`](https://ui.frontic.com/DESIGN.md#progress) |
| `indicator` | [`progress-indicator`](https://ui.frontic.com/DESIGN.md#progress-indicator) |

## Source

`components/ui/progress/Progress.vue`

```vue
<script lang="ts">
/** Shows how far a task has progressed, as a share of the whole. */
export default {}
</script>

<script setup lang="ts">
import type { ProgressRootProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { ProgressIndicator, ProgressRoot, useForwardProps } from 'reka-ui'
import { computed } from 'vue'

const props = withDefaults(
  defineProps<
    Omit<ProgressRootProps, 'modelValue'> & {
      /** How far the task is, from 0 to `max`; `null` while that is unknown, which slides a segment along the track. Can be bound with `v-model`. */
      modelValue?: number | null
      class?: HTMLAttributes['class']
    }
  >(),
  {
    modelValue: 0,
  },
)

const delegatedProps = reactiveOmit(props, 'class')
const filled = computed(() => (props.modelValue == null ? undefined : (props.modelValue / (props.max && props.max > 0 ? props.max : 100)) * 100))
const forwarded = useForwardProps(delegatedProps)
</script>

<template>
  <ProgressRoot data-slot="progress" v-bind="forwarded" :class="['fui-progress relative overflow-x-hidden', props.class]">
    <ProgressIndicator
      data-slot="progress-indicator"
      class="fui-progress-indicator size-full flex-1 transition-all data-state-indeterminate:animate-progress-indeterminate motion-reduce:data-state-indeterminate:animate-none motion-reduce:data-state-indeterminate:scale-x-33"
      :style="filled === undefined ? undefined : `transform: translateX(-${100 - filled}%);`"
    />
  </ProgressRoot>
</template>
```
