Skip to content

Progress

Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.

13%

## Installation
bash
npx @galaxy-stack/nebula-cli@latest add progress
bash
pnpm dlx @galaxy-stack/nebula-cli@latest add progress
bash
yarn dlx @galaxy-stack/nebula-cli@latest add progress
bash
bunx @galaxy-stack/nebula-cli@latest add progress
bash
# If you have installed galaxy-design globally
galaxy-design add progress

Usage

Basic

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

const progress = ref(33)
</script>

<template>
  <Progress :model-value="progress" />
</template>
tsx
import { Progress } from "@/components/ui/progress"

export default function ProgressDemo() {
  const [progress, setProgress] = React.useState(33)

  return <Progress :value={progress} />
}
typescript
import { Component, signal } from '@angular/core';
import { ProgressComponent } from '@/components/ui/progress';

@Component({
  selector: 'app-progress-demo',
  standalone: true,
  imports: [ProgressComponent],
  template: `<ui-progress [value]="progress()" />`
})
export class ProgressDemoComponent {
  progress = signal(33);
}

Examples

Default

A simple progress bar showing 33% completion.

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

<template>
  <Progress :model-value="33" />
</template>
tsx
import { Progress } from "@/components/ui/progress"

export default function App() {
  return <Progress value={33} />
}
typescript
import { Component } from '@angular/core';
import { ProgressComponent } from '@/components/ui/progress';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [ProgressComponent],
  template: `<ui-progress [value]="33" />`
})
export class AppComponent {}

With Label

Display progress with a percentage label.

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

const progress = ref(65)
</script>

<template>
  <div class="w-full space-y-2">
    <div class="flex items-center justify-between">
      <span class="text-sm font-medium">Progress</span>
      <span class="text-sm text-muted-foreground">{{ progress }}%</span>
    </div>
    <Progress :model-value="progress" />
  </div>
</template>
tsx
import { Progress } from "@/components/ui/progress"

export default function App() {
  const progress = 65

  return (
    <div className="w-full space-y-2">
      <div className="flex items-center justify-between">
        <span className="text-sm font-medium">Progress</span>
        <span className="text-sm text-muted-foreground">{progress}%</span>
      </div>
      <Progress value={progress} />
    </div>
  )
}
typescript
import { Component, signal } from '@angular/core';
import { ProgressComponent } from '@/components/ui/progress';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [ProgressComponent],
  template: `
    <div class="w-full space-y-2">
      <div class="flex items-center justify-between">
        <span class="text-sm font-medium">Progress</span>
        <span class="text-sm text-muted-foreground">{{ progress() }}%</span>
      </div>
      <ui-progress [value]="progress()" />
    </div>
  `
})
export class AppComponent {
  progress = signal(65);
}

Animated

Animate the progress value over time.

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

const progress = ref(0)

onMounted(() => {
  const timer = setInterval(() => {
    progress.value += 10
    if (progress.value >= 100) {
      clearInterval(timer)
    }
  }, 500)
})
</script>

<template>
  <Progress :model-value="progress" />
</template>
tsx
import { Progress } from "@/components/ui/progress"
import { useEffect, useState } from "react"

export default function App() {
  const [progress, setProgress] = useState(0)

  useEffect(() => {
    const timer = setInterval(() => {
      setProgress((prev) => {
        if (prev >= 100) {
          clearInterval(timer)
          return 100
        }
        return prev + 10
      })
    }, 500)

    return () => clearInterval(timer)
  }, [])

  return <Progress value={progress} />
}
typescript
import { Component, OnInit, OnDestroy, signal } from '@angular/core';
import { ProgressComponent } from '@/components/ui/progress';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [ProgressComponent],
  template: `<ui-progress [value]="progress()" />`
})
export class AppComponent implements OnInit, OnDestroy {
  progress = signal(0);
  private timer?: number;

  ngOnInit() {
    this.timer = window.setInterval(() => {
      this.progress.update(prev => {
        if (prev >= 100) {
          if (this.timer) clearInterval(this.timer);
          return 100;
        }
        return prev + 10;
      });
    }, 500);
  }

  ngOnDestroy() {
    if (this.timer) clearInterval(this.timer);
  }
}

Custom Max Value

Set a custom maximum value instead of the default 100.

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

<template>
  <div class="space-y-2">
    <p class="text-sm text-muted-foreground">75 out of 150 items completed</p>
    <Progress :model-value="75" :max="150" />
  </div>
</template>
tsx
import { Progress } from "@/components/ui/progress"

export default function App() {
  return (
    <div className="space-y-2">
      <p className="text-sm text-muted-foreground">75 out of 150 items completed</p>
      <Progress value={75} max={150} />
    </div>
  )
}
typescript
import { Component } from '@angular/core';
import { ProgressComponent } from '@/components/ui/progress';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [ProgressComponent],
  template: `
    <div class="space-y-2">
      <p class="text-sm text-muted-foreground">75 out of 150 items completed</p>
      <ui-progress [value]="75" [max]="150" />
    </div>
  `
})
export class AppComponent {}

API Reference

Props

PropTypeDefaultDescriptionFrameworks
valuenumber0Current progress valueAll
maxnumber100Maximum progress valueAll
getValueLabel(value: number, max: number) => string-Accessible label generator for the current valueReact, Vue, Angular
classNamestring''CSS class namesReact, React Native
classstring''CSS class namesVue, Angular, Flutter

Vue Events

EventTypeDescription
update:modelValue(value: number) => voidEmitted when the progress value changes

Accessibility

  • Built on Radix UI Progress primitive for React
  • Built on Radix Vue Progress primitive for Vue
  • Built on Radix NG Progress primitive for Angular
  • Follows WAI-ARIA design pattern for progress indicators
  • Properly announces progress changes to screen readers

Notes

  • The progress value is automatically clamped between 0 and max
  • The indicator animates smoothly when the value changes
  • For indeterminate progress (loading without known duration), consider using a Spinner component instead

Author

Bùi Trọng Hiếu (kevinbui)

License

MIT © 2025 Bùi Trọng Hiếu (kevinbui)

Released under the MIT License.