Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Progress Bar Laravel Package

haythem/progress-bar

A lightweight Laravel package for displaying customizable progress bars in Blade views. Configure colors, sizes, and values to visualize task completion, loading states, or any percentage-based progress with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Laravel Nova field package — not a general Laravel package. It only does anything inside a Nova resource, dashboard, or tool. If you don't already have a Nova app (and a Nova license/nova private repo configured in composer.json), there is nothing here for you to evaluate yet.

Minimal steps:

composer require haythem/progress-bar
php artisan nova:publish   # if the field's compiled assets don't show up

Nova fields register their JS/CSS through their service provider (Nova::script / Nova::style), so there is normally no manual provider registration — Laravel package discovery handles it. If the field renders as a blank cell, that registration step is the first thing to confirm.

First use case — drop it into a resource's fields() and feed it a number:

use Haythem\ProgressBar\ProgressBar;

public function fields(NovaRequest $request)
{
    return [
        ProgressBar::make('Onboarding')
            ->resolveUsing(fn () => $this->completedSteps() / max($this->totalSteps(), 1) * 100)
            ->help('Percent complete'),
    ];
}

Where to look first, in this order:

  1. README.md — for the exact chainable API (this package is small; the README is the spec).
  2. src/ProgressBar.php — the field class. Everything that matters is here: $component, default meta, and any custom fluent setters.
  3. resources/js/components/*.vue (or dist/js/field.js if source isn't shipped) — the props it actually reads. Method names in the PHP class must match props in the Vue component; that correspondence is the whole API.
  4. composer.json — check the nova version constraint before planning anything.

Because the package is tiny and single-maintainer, budget time to read all four. That's an afternoon, not a week.

Implementation Patterns

Compute in the model, display in the field. The most robust pattern is to make the field dumb and put the math in an accessor or a resolveUsing closure. That keeps the percentage testable without booting Nova or a browser:

// Model
public function getStoragePercentAttribute(): float
{
    return $this->quota_bytes > 0
        ? round($this->used_bytes / $this->quota_bytes * 100, 1)
        : 0.0;
}

// Nova
ProgressBar::make('Storage', 'storage_percent')
    ->displayUsing(fn ($value) => $value . '%'),

Per-row progress on index views. This is the pattern people actually want — a progress bar per row in a resource index. It works, but only if the underlying numbers are already loaded. Pair it with withCount/aggregates on the resource's indexQuery, otherwise you get one query per row computing the percentage:

public static function indexQuery(NovaRequest $request, $query)
{
    return $query->withSum('tasks as done_tasks', 'completed')
                 ->withCount('tasks');
}

Detail-page layout. Wrap it in a Panel or Section so it reads as a metric rather than a stray field. It's most useful next to supporting numbers (Number::make('Used'), Number::make('Quota')) — a bar with no denominator visible is decoration.

Read-only by default. A progress bar is an output, not an input. Apply ->exceptOnForms() (or ->readonly()) so it doesn't appear in create/update forms as a confusing empty control.

Thresholds and colors. If the field exposes color meta, drive it from the model so business rules live in PHP, not in a Vue file you'd have to rebuild:

ProgressBar::make('Health')
    ->withMeta(['color' => $this->health_percent > 80 ? '#22c55e' : '#ef4444'])

When you outgrow it. The realistic path for a 1-star field is: use it for a week, then copy src/ and the Vue component into nova-components/ProgressBar with php artisan nova:field. You keep the behavior, gain a build you control, and stop depending on an unmaintained repo.

Gotchas and Tips

This package has one star and zero dependents. Treat it as a code sample, not infrastructure. Read every line of src/ProgressBar.php before shipping it; it is short enough that this is cheap. If you need it long-term, vendor-fork it into your own nova-components/ directory and note the origin in a comment.

Nova major-version mismatch is the most likely failure. Nova 4 (Laravel Mix) and Nova 5 (Vite) ship different asset pipelines. Check the nova constraint in composer.json against your installed version, and check whether dist/ exists or whether you're expected to npm install && npm run build inside vendor/. The latter is a smell — it means the release is not actually shippable as-is.

Blank field / no styles. Almost always one of: (a) the service provider didn't register the script, (b) assets need php artisan nova:publish, (c) the $component name in the PHP class doesn't match the registered Vue component name. Check the browser network tab for a 404 on the field JS before touching PHP.

Percent vs fraction. The single most common visual bug with progress bars is a 0–1 value fed to something expecting 0–100 (or vice versa) — you get either a permanently full bar or a sliver. Clamp defensively: max(0, min(100, $value)). Don't trust the model to always be in range.

Nulls and division by zero. A null value typically renders as 0% or an empty cell with no hint that data is missing. Coalesce to a sensible default and consider ->nullable()-style display text so "no data" is visually distinct from "0% complete."

Sorting and filtering don't exist here. A custom field is display-only. ->sortable() on a computed percentage will either error or silently sort by a nonexistent column. If you need sortable/filterable progress, persist the percentage to a real column (or computed column) and sort on that.

Index-view N+1. As above — the field resolves per row. Any resolveUsing that touches a relationship will fire per row. Verify with DB::listen or Telescope on a 50-row index before declaring victory.

No reactivity. Nova fields re-render on page load/navigation, not on data change. There's no built-in polling. If you want a live bar, either refresh the detail page or wire a setInterval + Nova.request() in the component — at which point you own the Vue code anyway.

Extension points are thin. You can override ->component('my-custom-bar') and register your own compiled component, and you can pass arbitrary data through ->withMeta([...]). Those two hooks cover most customization; anything beyond that is a fork.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle