Introduction
The Progress component provides a flexible and accessible way to display progress indicators, loading states, and completion tracking. It supports both determinate (known progress) and indeterminate (unknown duration) states, buffer visualization for streaming content, and extensive customization options. Perfect for file uploads, form completion, video buffering, data processing, and any scenario requiring progress visualization.
Installation
Use the Sheaf artisan command to install the progress component:
php artisan sheaf:install progress
Once installed, you can use
<x-ui.progress />in any Blade view.
Basic Usage
<!-- Static progress values --> <div class="space-y-2"> <x-ui.text size="sm" class="font-medium">25% Progress</x-ui.text> <x-ui.progress value="25" /> </div> <x-ui.progress value="50" /> <x-ui.progress value="75" /> <x-ui.progress value="100" />
Size Variants
The progress bar comes in five sizes, from extra small to extra large. Control the height using the size prop:
<x-ui.progress value="65" size="xs" /> <x-ui.progress value="65" size="sm" /> <x-ui.progress value="65" size="md" /> <x-ui.progress value="65" size="lg" /> <x-ui.progress value="65" size="xl" />
Livewire Integration
Bind the progress component to Livewire state using wire:model.live for real-time updates:
<div> <x-ui.text>Upload Progress: {{ $uploadProgress }}%</x-ui.text> <x-ui.progress wire:model.live="uploadProgress" /> <x-ui.button wire:click="simulateUpload"> Start Upload </x-ui.button> </div>
Alpine.js Integration
Use x-model for client-side reactive progress updates. This works great for pure frontend interactions or in combination with Livewire:
<div x-data="{ progress: 50 }"> <x-ui.progress x-model="progress" /> <x-ui.button x-on:click="progress = Math.min(progress + 10, 100)" size="sm"> +10% </x-ui.button> <x-ui.button x-on:click="progress = Math.max(progress - 10, 0)" size="sm" variant="outline"> -10% </x-ui.button> <input type="range" x-model="progress" min="0" max="100" /> </div>
Customizing Bar Color
To use a different color from your theme's primary color, use the [&_[data-slot=bar]] selector with any Tailwind color:
<!-- Red --> <x-ui.progress value="70" class="[&_[data-slot=bar]]:bg-red-500" /> <!-- Amber --> <x-ui.progress value="70" class="[&_[data-slot=bar]]:bg-amber-500" />
Animated Progress
Simulate smooth progress updates using JavaScript's requestAnimationFrame() function for frame-perfect animations:
<div x-data="{ value: 0, animate() { const duration = 4000; const startTime = performance.now(); const updateProgress = (currentTime) => { const elapsed = currentTime - startTime; const progress = Math.min(elapsed / duration, 1); this.value = progress * 100; if (progress < 1) { requestAnimationFrame(updateProgress); } else { setTimeout(() => { this.value = 0; this.animate(); }, 1000); } }; requestAnimationFrame(updateProgress); } }" x-init="animate()" > <x-ui.progress class="[&_[data-slot=bar]]:bg-amber-500" x-model="value" /> </div>
Wave Animation
Add a shimmer wave effect to provide visual feedback during slow or long-running progress updates:
<!-- Wave animation provides visual feedback on slow progress --> <x-ui.progress class="[&_[data-slot=bar]]:bg-orange-500" value="35" wave /> <x-ui.progress class="[&_[data-slot=bar]]:bg-green-500" value="15" wave size="lg" />
Note: The wave animation uses a white gradient overlay, so it may not be visible on light backgrounds or when using light colors like the default white primary color.
Top and Bottom Slots
Add custom content above or below the progress bar using named slots:
<x-ui.progress value="65"> <x-slot:top> <div class="flex justify-between mb-2"> <x-ui.text>Uploading files...</x-ui.text> <x-ui.text>65%</x-ui.text> </div> </x-slot:top> <x-slot:bottom> <div class="flex justify-between mt-2"> <x-ui.text size="xs">2.4 MB of 5.3 MB</x-ui.text> <x-ui.text size="xs">~2 min remaining</x-ui.text> </div> </x-slot:bottom> </x-ui.progress>
Advanced Features
The progress component supports compound state objects for advanced use cases like video buffering and indeterminate loading.
Understanding State Formats
The progress component accepts state in three different formats:
1. Simple Integer (Basic Progress)
public $progress = 50; // Just a number
2. Compound Object (Advanced Features)
// Value + Buffer (for streaming content) public $progress = ['value' => 50, 'buffer' => 75]; // Value + Indeterminate (for unknown duration tasks) public $progress = ['value' => 0, 'indeterminate' => true]; // All properties combined public $progress = [ 'value' => 50, 'buffer' => 75, 'indeterminate' => false ];
State Rules:
bufferautomatically adjusts to never be less thanvalue- When
indeterminate: true, the bar displays an animated loading state regardless of value - All values are automatically clamped between
minandmax
Dynamic Color Transitions
Create progress bars that smoothly transition colors based on completion percentage:
<div x-data="{ value: 0, currentColor: 'rgb(239, 68, 68)', getProgressColor() { const percent = this.value; let r, g, b; if (percent < 25) { const ratio = percent / 25; r = 239; g = Math.floor(68 + (147 * ratio)); b = 68; } else if (percent < 50) { const ratio = (percent - 25) / 25; r = Math.floor(239 + (15 * ratio)); g = Math.floor(215 + (25 * ratio)); b = Math.floor(68 - (54 * ratio)); } else if (percent < 75) { const ratio = (percent - 50) / 25; r = Math.floor(254 - (122 * ratio)); g = Math.floor(240 - (24 * ratio)); b = Math.floor(14 + (8 * ratio)); } else { const ratio = (percent - 75) / 25; r = Math.floor(132 - (10 * ratio)); g = Math.floor(216 + (28 * ratio)); b = Math.floor(22 + (43 * ratio)); } return `rgb(${r}, ${g}, ${b})`; } }" > <x-ui.progress x-model="value" x-bind:style="`--color-primary: ${currentColor}`" wave /> </div>
Note: While color spaces like
oklchorHSLprovide better color interpolation, I've used RGB here for performance and simplicity in dynamically generating specific color values.
Static Color Switching
Switch between predefined colors based on progress value using Alpine's reactive classes:
<x-ui.progress x-model="progress" x-bind:class="{ '[&_[data-slot=bar]]:bg-red-500': progress < 25, '[&_[data-slot=bar]]:bg-orange-500': progress >= 25 && progress < 50, '[&_[data-slot=bar]]:bg-yellow-500': progress >= 50 && progress < 75, '[&_[data-slot=bar]]:bg-green-500': progress >= 75 }" />
Buffer Progress
Display dual progress bars for buffering scenarios like video players or file downloads. Pass a compound object with both value and buffer properties:
<!-- Alpine.js --> <div x-data="{ progress: {value: 12, buffer: 34 }}"> <x-ui.progress x-model="progress" /> </div> <!-- Livewire --> <!-- public array $progress = ['value' => 12, 'buffer' => 34] --> <x-ui.progress wire:model.live="progress" />
How it works: The buffer bar always stays ahead of or equal to the current value. If you try to set the value higher than the buffer, the buffer automatically adjusts upward.
Indeterminate Loading
For tasks with unknown duration, use the indeterminate property to display an animated loading state:
<!-- Livewire --> <!-- public array $progress = ['value' => 0, 'indeterminate' => true] --> <x-ui.progress wire:model.live="progress" /> <!-- Alpine.js --> <div x-data="{progress: {value: 0, indeterminate: true}}"> <x-ui.progress x-model="progress" /> </div>
Complete Example: Video Buffering with Indeterminate Support
This example demonstrates using all compound properties together:
<!-- Livewire --> <!-- public array $progress = ['value' => 12, 'buffer' => 46, 'indeterminate' => false] --> <x-ui.progress wire:model.live="progress" /> <x-ui.button wire:click="$toggle('progress.indeterminate')"> Toggle Indeterminate </x-ui.button> <!-- Alpine.js --> <div x-data="{progress: {value: 12, buffer: 46, indeterminate: false}}"> <x-ui.progress x-model="progress" /> <x-ui.button x-on:click="progress.indeterminate = !progress.indeterminate"> Toggle Indeterminate </x-ui.button> </div>
Component Props
ui.progress
| Prop | Type | Default | Description |
|---|---|---|---|
value |
number | 0 |
Current progress value (0-100) |
max |
number | 100 |
Maximum value for progress calculation |
min |
number | 0 |
Minimum value for progress calculation |
size |
string | 'md' |
Bar height: 'xs', 'sm', 'md', 'lg', 'xl' |
buffer |
number|null | null |
Buffer progress value for dual progress bars |
wave |
boolean | false |
Enable shimmer wave animation |
duration |
number | 300 |
Transition duration in milliseconds |
top |
slot | - | Custom content displayed above the progress bar |
bottom |
slot | - | Custom content displayed below the progress bar |
Styling
The progress component provides multiple ways to customize colors:
Using CSS Custom Properties
<!-- Static CSS variable --> <x-ui.progress value="60" style="--color-primary: rgb(59, 130, 246)" /> <!-- Dynamic color with Alpine --> <x-ui.progress x-model="progress" x-bind:style="`--color-primary: ${dynamicColor}`" />
Using Data-Slot Selector
<!-- Static color --> <x-ui.progress value="60" class="[&_[data-slot=bar]]:bg-purple-500" /> <!-- Conditional colors with Alpine --> <x-ui.progress x-model="progress" x-bind:class="{ '[&_[data-slot=bar]]:bg-red-500': progress < 50, '[&_[data-slot=bar]]:bg-green-500': progress >= 50 }" />