ryangjchandler/filament-progress-column
Add a progress bar column to any Filament table. This package renders numeric values as customizable progress bars, with options for colors, heights, and labels, making percentages, completion rates, and capacity easy to visualize at a glance in your admin panel.
ryangjchandler/filament-progress-column is a single-class Filament table column that renders a horizontal progress bar from a numeric attribute instead of plain text. There is no plugin registration, no config file, and no service provider to publish — you install it and start using the column class directly.
composer require ryangjchandler/filament-progress-column
Requirements as of v2.1.0: Filament v4 or v5 and Laravel 13 support was added in this release. If you are still on Filament v3, pin the 1.x line — v2 is tied to the Filament v4 column API rewrite.
Where to look first: the package README for the exact fluent methods on RyanChandler\FilamentProgressColumn\ProgressColumn, then your own app/Filament/Resources/*/Tables/*Table.php for a column you already display as a raw percentage.
First use case: you have a progress, completion, or uploaded_percent integer (0–100) in the database and are currently showing it with TextColumn::make('progress'). Swap the class:
use RyanChandler\FilamentProgressColumn\ProgressColumn;
ProgressColumn::make('progress')
That single change gets you the bar, Filament's standard column features (sorting, searching, ->toggleable()), and Filament's color palette for styling.
Threshold colouring. Drive the bar colour from the state so at-risk rows read instantly in a table of hundreds:
ProgressColumn::make('progress')
->color(fn (int $state) => match (true) {
$state >= 100 => 'success',
$state >= 50 => 'warning',
default => 'danger',
})
Reusable column object. If the same metric appears in a Resource table, a RelationManager, and a dashboard table widget, return a configured instance from a small helper so thresholds don't drift:
// app/Filament/Columns/ProgressBar.php
public static function make(string $name = 'progress'): ProgressColumn
{
return ProgressColumn::make($name)
->color(fn (int $state) => $state >= 100 ? 'success' : 'primary')
->tooltip(fn ($record) => "{$record->completed_steps} of {$record->total_steps} steps");
}
Pair it with the raw number. A bar alone is unreadable at a glance when rows are dense. Keep a narrow text column next to it, or use the tooltip above:
ProgressColumn::make('progress')
->grow(false),
TextColumn::make('progress')
->suffix('%')
->alignEnd(),
Live progress. For long-running jobs, let the table refresh so the bar actually moves without a manual reload — either on the table or scoped to the column's table:
->poll('5s')
Combine with ->toggleable(isToggledHiddenByDefault: true) on heavy columns so polling does not repaint more than necessary.
Normalise at the accessor, not in the view. If the underlying value is a fraction (0–1) or a count/total pair, expose a progress accessor on the model returning an int 0–100 and point the column at that. Keeps the column dumb and testable.
Testing. It is a normal Filament column, so Livewire table tests work unchanged: livewire(ListUsers::class)->assertTableColumnExists('progress') and assert on the rendered percentage text rather than the bar markup.
0.75 fraction renders as a sliver, and negative or >100 values can produce odd CSS widths. Multiply/clamp in the model accessor.null state renders an empty bar, which looks like "0% complete" and will mislead. Use ->default(0) or ->placeholder('—') and decide deliberately which one your users read as "not started".->sortable(['db_column']) or ->sortable(query: ...) so ordering doesn't fall back to an unindexed computed value.->grow(false) plus a fixed ->width() keeps it consistent across viewports.Plugin class, and the class name/namespace is the only integration point.filament/filament ^5.0, run composer why ryangjchandler/filament-progress-column after a Filament upgrade to confirm the package released its constraint rather than Composer silently holding you on Filament v4.Placeholder/ViewField with your own markup rather than trying to reuse this column.How can I help you explore Laravel packages today?