spatie/laravel-livewire-wizard
Build multi-step Livewire wizards where each step is its own component, with easy navigation control and full access to regular Livewire features.
Install the package and make sure your app is on Livewire 3+:
composer require spatie/laravel-livewire-wizard
There is no config file or published assets to worry about. The package is headless: you get two base classes and a small runtime for step navigation and state sharing.
Start by creating one wizard component and at least two step components.
use Spatie\LivewireWizard\Components\WizardComponent;
class CheckoutWizardComponent extends WizardComponent
{
public function steps(): array
{
return [
CartStepComponent::class,
DeliveryAddressStepComponent::class,
ConfirmOrderStepComponent::class,
];
}
}
use Spatie\LivewireWizard\Components\StepComponent;
class CartStepComponent extends StepComponent
{
public function render()
{
return view('checkout.steps.cart');
}
}
Render the wizard from any Blade page:
<livewire:checkout-wizard />
The first thing to build is a simple cart → address → confirm flow. Once that works, add validation, state sharing, and custom navigation. Look first at WizardComponent for lifecycle/navigation methods and StepComponent for step-side helpers; the demo app linked from the README is the fastest reference for a complete checkout flow.
Linear flow with step-local buttons. Put navigation inside each step's Blade view:
<button wire:click="previousStep">Back</button>
<button wire:click="nextStep">Next</button>
Steps can call nextStep(), previousStep(), and showStep(SomeStep::class) directly. Keep navigation logic in the step or in a small parent view partial if you want consistent chrome.
Jump navigation and progress indicators. Use the wizard's step list to render a progress bar. Check membership with hasStep() before offering a link, and use showStep() to jump. This is useful for edit-from-summary flows where the user clicks "Edit address" on the confirmation step.
Share state deliberately. Each step exposes a state(): array containing the values other steps need. Read another step's state with forStepClass() or read everything with allStepState():
public function state(): array
{
return ['email' => $this->email];
}
// in another step
$address = $this->state()->forStepClass(DeliveryAddressStepComponent::class);
Only include serializable values. Don't dump entire Eloquent models into state if a small DTO/array will do.
Guard transitions with validation. Override nextStep() in the step, validate, then call the parent:
public function nextStep()
{
$this->validate();
parent::nextStep();
}
This keeps validation close to the fields that own it and prevents users skipping required steps.
Dynamic step lists. Because steps() is a method, you can build the list conditionally from the authenticated user, cart contents, or feature flags. This is the main reason to prefer this package over a fixed form wizard. Guard downstream steps with hasStep() so a removed step doesn't break navigation.
Testing. The package exposes wizard testing helpers, and newer versions add StepComponent::testStep() for testing a step in isolation. Use these for navigation assertions and validation rules; fall back to plain Livewire component tests for the step's own rendering.
Integration with existing UI. Since the package is headless, wrap the wizard in your existing layout and use your own buttons, progress bar, and summary markup. If you need custom wrapper markup, override the wizard's render() or publish/override its view rather than fighting the default.
wire:key. Upgrade to at least 2.0.1.state(), you'll get confusing hydration errors. 2.4.3 added support for some synthesizable objects, but arrays and scalars remain the safest choice.nextStep() appears to do nothing, check that the step class returned by steps() matches the class name you pass to showStep(), and inspect the wizard's current-step property in Livewire devtools. Class-string typos fail silently in navigation rather than throwing.initialStep() to change the starting step, override render() for custom wizard chrome, and add methods on step components for anything domain-specific. Keep wizard-level logic thin; put business rules in step classes or services.How can I help you explore Laravel packages today?