sjwatts119 / livewire-wizard
A Simple Headless Livewire Wizard Component
Fund package maintenance!
Sam Watts
Requires
- php: ^8.2
- illuminate/contracts: ^11.0||^12.0
- livewire/livewire: ^3.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.1.1||^7.10.0
- orchestra/testbench: ^10.0.0||^9.0.0
- pestphp/pest: ^3.0
- pestphp/pest-plugin-arch: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
This package is auto-updated.
Last update: 2025-05-03 22:25:38 UTC
README
View Package Demo
Installation
Install the package via composer:
composer require sjwatts119/livewire-wizard
Creating Your First Wizard
To make your first wizard component, run the following command:
php artisan make:wizard
This command supports any arguments used in the Livewire Make Command.
A new wizard class and a corresponding step view will have been created. The class will look like this:
<?php namespace App\Livewire; use Illuminate\View\View; use SamWatts\LivewireWizard\Livewire\Wizard; use SamWatts\LivewireWizard\Wizard\WizardStep; class YourWizard extends Wizard { public function wizardSteps(): array { return [ WizardStep::make( title: 'Step 1', view: view('step-1'), ), ]; } public function render(): View { return $this ->currentStep() ->view(); } }
Creating Wizard Steps
To create a new Wizard Step, you can add a new WizardStep
instance in the wizardSteps()
array.
A WizardStep
accepts:
title
: The name of the step. This must be unique from your other steps.view
: The view to be rendered when the step is active.canNavigate
(Optional): A boolean closure, prevents access to a step unless the closure evaluates totrue
. For more information, see Defining Step Rules.
Here is an example of a contact form wizard with two steps:
<?php namespace App\Livewire; use Illuminate\View\View; use SamWatts\LivewireWizard\Livewire\Wizard; use SamWatts\LivewireWizard\Wizard\WizardStep; class YourWizard extends Wizard { public function wizardSteps(): array { return [ WizardStep::make( title: 'Your Message', view: view('livewire.wizard.message'), ), WizardStep::make( title: 'Your Details', view: view('livewire.wizard.details'), ), ]; } public function render(): View { return $this ->currentStep() ->view(); } }
Defining Step Rules
As mentioned previously, a WizardStep can accept a canNavigate
closure. This closure will be called when the step is rendered, and should return a boolean value.
The wizard class exposes a helpful method validatePropertiesForStep()
which can be used to validate any number of class properties. Internally, this uses Livewire's Validation to validate the properties, and returns a boolean value indicating whether the validation passed for all provided properties.
This validation also works with Livewire Form Object Properties. These should be referenced using dot notation, e.g. form.name
.
For example, the following step will only be accessible if the validation rules defined on the name and message properties pass:
<?php namespace App\Livewire; use Illuminate\View\View; use SamWatts\LivewireWizard\Livewire\Wizard; use SamWatts\LivewireWizard\Wizard\WizardStep; class YourWizard extends Wizard { #[Validate('required')] public string $name = ''; #[Validate('required')] public string $message = ''; public function wizardSteps(): array { return [ // Previous steps... WizardStep::make( title: 'Confirm', view: view('livewire.wizard.confirm'), canNavigate: fn () => $this->validatePropertiesForStep(['name', 'message']), ), ]; } public function render(): View { return $this ->currentStep() ->authorise() // Validate the 'name' and 'message' properties if we are on step 'Confirm'. ->view(); } }
Rendering The Wizard
If you'd like to retrieve the view for the current step, you can call:
$this->currentStep()->view();
If any of your steps have canNavigate
closures, you can run these before rendering the view:
$this->currentStep() ->authorise() ->view();
The authorise()
method will abort with a 403 response if the closure returns false.
Optionally, you can pass a false boolean value to the aborts
parameter of authorise()
.
$this->currentStep() ->authorise(aborts: false) ->view();
This will cause a StepNotAuthorisedException
to be thrown when the canNavigate
closure returns false instead of aborting the request. You could then gracefully handle this exception using Livewire's Exception Lifecycle Hook. For example:
namespace App\Livewire; use Illuminate\View\View; use SamWatts\LivewireWizard\Livewire\Wizard; use SamWatts\LivewireWizard\Wizard\WizardStep; use SamWatts\LivewireWizard\Exceptions\StepNotAuthorisedException; class YourWizard extends Wizard { /* * Gracefully handle the exception. */ public function exception($e, $stopPropagation) { if ($e instanceof StepNotAuthorisedException) { $this->notify('Post is not found'); $stopPropagation(); } } public function wizardSteps(): array { return [ // Steps with canNavigate closures... ]; } public function render(): View { return $this ->currentStep() ->authorise(aborts: false) // Throws StepNotAuthorisedException if rules fail... ->view(); } }
Navigation
In your step views, you likely want to add some navigation buttons to allow the user to move between steps.
You could use the wire:click directive to call any of the navigation methods on the wizard class. For example:
<!-- Navigating to a specific step --> <x-wizard.nav.item :current="$currentStep->is($step)" :disabled="!$step->canNavigate()" wire:click="navigateToStep('{{ $step->title() }}')" > {{ $step->title() }} </x-wizard.nav.item> <!-- Navigating to the next step --> <x-wizard.nav.item :disabled="!$nextStep->canNavigate()" wire:click="navigateToNextStep()" > Next </x-wizard.nav.item> <!-- Navigating to the previous step --> <x-wizard.nav.item :disabled="!$previousStep->canNavigate()" wire:click="navigateToPreviousStep()" > Back </x-wizard.nav.item>
Wizard Methods
The wizard component has a number of public methods which can be used to get information about the current state of the wizard. These methods are:
$this->steps(); // Returns a Collection of all WizardStep instances, keyed by their titles.
$this->currentStep(); // Returns the current WizardStep instance.
$this->nextStep(); // Returns the next WizardStep instance, or null if one does not exist.
$this->previousStep(); // Returns the previous WizardStep instance, or null if one does not exist.
$this->firstStep(); // Returns the first WizardStep instance.
$this->lastStep(); // Returns the last WizardStep instance.
$this->step('step-title'); // Returns the WizardStep instance with the given title, or null if it does not exist.
Wizard Navigation Methods
You can use the following methods to navigate between steps in the wizard:
$this->navigateToPreviousStep(); // Sets the current step to the previous step, or does nothing if one does not exist.
$this->navigateToNextStep(); // Sets the current step to the next step, or does nothing if one does not exist.
$this->navigateToStep('step-title'); // Sets the current step to the WizardStep instance with the given title, or does nothing if it does not exist.
WizardStep Methods
The WizardStep
class has a number of methods which can be used to render the step in your view. These methods are:
$step->title(); // Returns the title of the step.
$step->view(); // Returns the view of the step.
$step->is($otherStep); // Returns a boolean value indicating whether the provided WizardStep has the same title as this step.
$step->authorise(); // Executes the canNavigate closure and returns the step instance.
$step->canNavigate(); // Executes the canNavigate closure and returns a boolean value.
Running Tests
To run the tests, install dependencies:
composer install
Then run the tests using:
composer test
Credits
License
The MIT License (MIT). Please see License File for more information.