spatie/laravel-livewire-wizard
Lightweight Livewire components for building multi-step wizards in Laravel. Define a wizard with an ordered list of step components, each with its own screen and Livewire logic, and guide users through checkout-style flows with ease.
Installation:
composer require spatie/laravel-livewire-wizard
Publish the config (if needed):
php artisan vendor:publish --provider="Spatie\LivewireWizard\LivewireWizardServiceProvider"
First Use Case: Create a wizard component:
php artisan make:livewire MultiStepWizard
Define steps in MultiStepWizard.php:
use Spatie\LivewireWizard\Traits\HasSteps;
class MultiStepWizard extends Component
{
use HasSteps;
public function mount()
{
$this->steps = [
'step1' => 'First Step',
'step2' => 'Second Step',
'step3' => 'Final Step',
];
}
}
Blade Integration:
<livewire:multi-step-wizard />
tests/ folder in the package for real-world usage patterns.config/livewire-wizard.php for global settings (e.g., default step transitions).Define Steps:
Use the HasSteps trait to declare steps programmatically or via config:
$this->steps = [
'step1' => [
'name' => 'Personal Info',
'component' => 'personal-info-step',
],
'step2' => [
'name' => 'Address',
'component' => 'address-step',
'canGoBack' => false, // Optional
],
];
Step Components:
Create Livewire components for each step (e.g., PersonalInfoStep.php):
class PersonalInfoStep extends Component
{
public $name;
public $email;
public function submit()
{
$this->dispatch('step-submitted', data: ['name' => $this->name]);
}
}
Navigation Logic: Use the wizard's built-in methods:
public function nextStep()
{
$this->goToNextStep();
}
public function previousStep()
{
$this->goToPreviousStep();
}
Dynamic Steps: Load steps dynamically (e.g., from a database):
public function mount()
{
$this->steps = Step::query()->pluck('name', 'slug')->toArray();
}
Validation: Validate step data before transitioning:
public function nextStep()
{
$this->validate(['email' => 'required|email']);
$this->goToNextStep();
}
Progress Tracking:
Use the currentStep property to show progress bars or indicators:
<div class="progress">
@foreach ($steps as $step => $name)
<div class="step {{ $step === $currentStep ? 'active' : '' }}">
{{ $name }}
</div>
@endforeach
</div>
State Management:
Pass data between steps via wire:model or events:
// In Step 1
$this->dispatch('update-step-data', data: ['email' => $this->email]);
// In Step 2 (listen in Blade)
<script>
window.addEventListener('update-step-data', event => {
@this.set('email', event.detail.data.email);
});
</script>
Conditional Steps: Hide/show steps based on logic:
public function getStepsProperty()
{
$steps = [...];
if ($this->isAdmin) {
$steps['admin-step'] = 'Admin Settings';
}
return $steps;
}
Step Component Registration:
steps array. Forgetting this will cause blank screens or errors.resources/views/livewire/ or are properly namespaced.State Persistence:
$this->persistentData or use Laravel's session:
session()->put('wizard_data', $this->stepData);
Event Conflicts:
step-submitted) may conflict with other Livewire components.$this->dispatch('wizard.step-submitted', data: [...]);
Validation Timing:
public function nextStep()
{
$this->validate();
$this->goToNextStep();
}
Step Not Rendering:
currentStep property in the browser's dev tools. If it’s null, the wizard isn’t initialized.{{ dd($currentStep) }} to the wizard blade template.Stuck Steps:
steps array to isolate the issue.Transition Issues:
wire:ignore on dynamic content that might interfere with transitions:
<div wire:ignore>
{{ $dynamicContent }}
</div>
Custom Transitions:
Override goToNextStep() or goToPreviousStep() to add logic:
public function goToNextStep()
{
if (!$this->canProceed()) {
session()->flash('error', 'Cannot proceed.');
return;
}
parent::goToNextStep();
}
Step Metadata: Extend step definitions with custom attributes:
$this->steps = [
'step1' => [
'name' => 'Intro',
'component' => 'intro-step',
'icon' => 'heroicon-o-information-circle',
'requiresLogin' => true,
],
];
Global Wizard Config:
Customize default behavior in config/livewire-wizard.php:
'default_step' => 'step1',
'allow_skip' => true,
'persist_data' => false,
Testing:
Use the LivewireWizardTestCase (if provided) or mock the trait:
$wizard = new class extends LivewireComponent {
use HasSteps;
public $steps = ['test' => 'Test Step'];
};
$wizard->goToNextStep();
$this->assertEquals('test', $wizard->currentStep);
Reusable Wizards: Create a base wizard class to avoid repetition:
class BaseWizard extends Component
{
use HasSteps;
public function render()
{
return view('wizards.base', ['steps' => $this->steps]);
}
}
Localization: Localize step names dynamically:
'name' => __("wizard.steps.{$step}"),
Performance: Lazy-load step components if they’re resource-intensive:
public function mount()
{
$this->steps = collect(config('wizard.steps'))
->mapWithKeys(fn ($step) => [$step['slug'] => $step])
->toArray();
}
How can I help you explore Laravel packages today?