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

Laravel Multi Step Progressbar Laravel Package

genealabs/laravel-multi-step-progressbar

A Laravel package by GeneaLabs for adding configurable multi-step progress bars to your app, perfect for multi-step forms and wizards. It offers a simple API and Blade component to track the current step, with customizable labels and styling, and requires minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a presentational Blade component: it renders the "Step 2 of 4" indicator and nothing else. Install it, check the constraint, publish the view, render it once in a layout.

composer require genealabs/laravel-multi-step-progressbar

Before writing any markup, open vendor/genealabs/laravel-multi-step-progressbar/composer.json and confirm the illuminate/* constraint. The 10.0.0 tag (May 2023) declares Laravel 10 support and nothing after it; if you are on 11/12, either test in a throwaway branch or plan to vendor the Blade file immediately (see Gotchas).

Where to look first, in order:

  1. The package README for the exact component tag and prop names — do not guess these.
  2. The package's Blade view(s) under vendor/.../resources/views/. This is a few dozen lines of markup; read all of it before adopting. It tells you the real API, the CSS classes it emits, and the accessibility semantics (or lack of them).
  3. src/ for the component class and any view-model — that is the entire encapsulated surface.
  4. The service provider, only to confirm whether views/config are publishable and under what tags.

Publish views so styling is yours, not upstream's:

php artisan vendor:publish --provider="GeneaLabs\MultiStepProgressbar\..." --tag=views

(Confirm the provider FQCN and tag from the package; providers are not always auto-discovered for view-only packages.)

First use case: a checkout or onboarding wizard you already have routing for. Define the steps once, derive the current step from the request, render the bar in the wizard layout:

// app/Support/Wizards/CheckoutWizard.php
final class CheckoutWizard
{
    public const STEPS = [
        'checkout.cart'     => 'Cart',
        'checkout.shipping' => 'Shipping',
        'checkout.payment'  => 'Payment',
        'checkout.review'   => 'Review',
        'checkout.confirm'  => 'Confirm',
    ];

    public static function current(): int
    {
        $index = array_search(request()->route()?->getName(), array_keys(self::STEPS), true);

        return $index === false ? 0 : $index;
    }
}
{{-- resources/views/layouts/checkout.blade.php --}}
<x-multi-step-progressbar :steps="array_values(\App\Support\Wizards\CheckoutWizard::STEPS)" :current="\App\Support\Wizards\CheckoutWizard::current()" />

If the prop names differ in your version, adapt — the pattern holds. That is the whole integration. No migrations, no queue, no container bindings, no HTTP surface.

Implementation Patterns

Keep all wizard state outside the package. The component takes step labels and an index. Everything else — which step is allowed, what happens on back-navigation, whether a draft is resumable — belongs in your controllers, form requests, or a small state machine. Do not try to push gating into the stepper; it has no concept of it.

Single source of truth for steps. The same array must drive three things: the stepper labels, the route guard that rejects out-of-order access, and the analytics events. Put it in one class (as above) or in a dedicated config file if steps are truly static. If labels need translation, use keys and resolve in the view:

public const STEPS = ['cart', 'shipping', 'payment', 'review', 'confirm'];
// __("checkout.steps.{$key}")

Wrap it in your own component, always. This is the single highest-value pattern with an unmaintained dependency — it makes the package swappable for a one-file delete later:

{{-- resources/views/components/app/stepper.blade.php --}}
@props(['current', 'steps' => null])

@php($steps = $steps ?? collect(\App\Support\Wizards\CheckoutWizard::STEPS)->map(fn ($l) => __("checkout.steps.{$l}"))->all())

<x-multi-step-progressbar :steps="$steps" :current="$current" {{ $attributes }} />

Every wizard then uses <x-app.stepper :current="$current" />, and no view outside this file knows the package exists. When you fork or rebuild, you change one file.

Derive current from the route, not from a session variable. Session-driven indices drift the moment a user bookmarks, refreshes, or hits Back. Route-name mapping (shown above) is idempotent and deep-linkable for free. If steps are not all routable (conditional KYC branches), filter the array before passing it in and keep a stable map from route → step key.

Roll out behind a flag on one flow first. The visual output will not match your design tokens out of the box. Prove it on checkout or an internal admin wizard, QA at mobile breakpoints, then propagate. Each additional wizard that adopts it deepens the override footprint.

Emit step-transition analytics from your own layer. The component renders state; it does not measure it. Fire an event in the controller or middleware when the route changes:

event(new WizardStepViewed($flow, $stepKey, $stepIndex, auth()->id()));

This also keeps you free to A/B numbered vs. labelled vs. percent indicators without touching the package.

Testing. Treat it as markup. Feature tests should assert on the content your layer controls, not on the package's class names:

$response->assertSee('Shipping')->assertSee('Payment');
// plus whatever ARIA attributes you added — see Gotchas

Do add one rendering smoke test per wizard layout; it catches a Laravel upgrade breaking the component faster than a manual pass.

Inertia/Livewire stacks. A Blade component is the wrong layer for an Inertia/React/Vue front end. Livewire is workable — render the stepper server-side in the Livewire view and re-render as current changes — but you are paying a Composer dependency for markup you could inline in a Livewire partial in twenty minutes.

Gotchas and Tips

Pin the exact version and vendor the view. With 63 stars, zero dependents, one maintainer, and no release since May 2023, treat this as fork-and-own from day one. composer require genealabs/laravel-multi-step-progressbar:10.0.0, publish the view, copy it into resources/views/components/, and plan to drop the Composer dependency once you have styled it. A cosmetic component must never gate a Laravel major upgrade — that is the realistic failure mode here.

The package does not do wizard logic, and roadmap scoping goes wrong here. Conditional steps, validation gating, draft persistence, resumability, back-navigation rules, deep-linkable steps: none of it is in the box. A "guided onboarding wizard" estimate that assumes this package covers the work is off by most of the work.

Accessibility is on you. Progress indicators have a defined ARIA pattern and a sparsely-maintained visual component is exactly where it ships missing. After publishing the view, add:

  • an ordered list (<ol>) for the steps, not a row of <div>s
  • aria-current="step" on the active item
  • role="progressbar" with aria-valuenow / aria-valuemin / aria-valuemax on the container, or aria-label="Step 2 of 5" if you go the text route
  • a polite live region if the indicator updates without a page load (Livewire, AJAX transitions)

Budget an accessibility and contrast audit before this sits on a conversion path with WCAG or enterprise-compliance commitments.

CSS collisions will cause more visible defects than any framework issue. The emitted classes may not survive Tailwind's purge if they are constructed dynamically, and the package's defaults will fight your tokens. Safelist or, better, rewrite the published view's classes to your design system's step tokens. Centralize them — per-feature overrides accumulating across wizards is precisely the inconsistency that standardizing on a stepper was meant to prevent.

Published views shadow package updates silently. Once you publish, upstream view changes never reach you. Diff the published file against the vendor copy when you touch the dependency, or skip publishing by fully vendoring — the same outcome with fewer surprises.

Debugging checklist:

  • Component not resolving → confirm the provider is registered/discovered; check php artisan about for the package.
  • Stale published view → php artisan view:clear && php artisan view:cache.
  • Wrong step highlighted → dump the array you passed; array_search returns 0 for the first key and false for a miss, so always compare with === false (an unguarded array_search result silently renders step 1 for every unmatched route).
  • Markup looks right but layout breaks → inspect computed styles against your tokens before assuming a package bug.

Do not pass translated strings into the step array at definition time. Resolve labels in the view (or the wrapper component) so a locale switch or a queue-rendered mail view does not bake in the wrong language — and so the array stays usable for route-name mapping, which must be locale-independent.

No localization, RTL, or vendor support is evidenced. If any of those are requirements, the honest choice is to lift the pattern into an internal component rather than adopt the dependency. Reading a few dozen lines of Blade to settle icon layout and completed-state styling is cheaper than the override work.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin