invelity / laravel-headless-wizard
Headless multi-step wizard package for Laravel with FormRequest validation, progress tracking, and frontend-agnostic API
Package info
github.com/invelity/laravel-headless-wizard
pkg:composer/invelity/laravel-headless-wizard
Requires
- php: ^8.4
- laravel/framework: ^12.0||^13.0
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.24
- nunomaduro/collision: ^8.8
- orchestra/testbench: ^10.0.0||^11.0.0
- pestphp/pest: ^4.0||^5.0
- pestphp/pest-plugin-arch: ^4.0||^5.0
- pestphp/pest-plugin-laravel: ^4.0||^5.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
- dev-main / 2.x-dev
- v2.1.0
- v2.0.0
- 1.x-dev
- v1.4.1
- v1.4.0
- v1.3.1
- v1.3.0
- v1.2.0
- v1.1.0
- v1.0.0
- dev-feat/make-step-view
- dev-docs/changelog-1.4.1
- dev-fix/1.x-scoped-storage
- dev-docs/fix-liquid
- dev-docs/v2
- dev-chore/v2-toolchain
- dev-feat/v2-generators
- dev-feat/v2-http
- dev-refactor/v2-runtime
- dev-refactor/v2-stores
- dev-refactor/v2-core
- dev-refactor/remove-frontend-helpers
- dev-chore/repository-hygiene
- dev-fix/routes-enabled
- dev-feat/laravel-13-support
- dev-refactor/solid-audit-cleanup
- dev-003-upgrade-pest-4
- dev-fix/update-command-tests
- dev-copilot/fix-ci-job-errors
This package is auto-updated.
Last update: 2026-10-06 12:07:56 UTC
README
Multi-step wizards for Laravel, built the way Laravel itself is built. You declare a wizard and its steps as classes. Every step validates its input with a form request. The package keeps each visitor's progress and tells you which step comes next. The rendering stays yours: Blade, Livewire, Inertia, Vue, React, or a mobile app talking to the JSON API.
use App\Wizards\OrderWizard; public function store(Request $request, OrderWizard $wizard, string $step) { $wizard->process($step, $request); // validated by the step's form request return to_route('order.step', $wizard->current()->id()); }
Features
- Typed and stateless.
OrderWizard $wizardis injected like a form request and bound to the current visitor. There is noinitialize()and no string ids. - Laravel-native validation. Each step names a form request, and the request runs its whole lifecycle:
prepareForValidation(),authorize(),after()hooks andpassedValidation(). Only validated data is stored. - Flow rules in one place. Optional steps, conditional steps (
shouldSkip()), dependencies that reopen later steps when earlier data changes, and display-only steps such as a confirmation page. - Scoped stores. Session, cache, database (encrypted) and array stores. Every visitor's state is kept apart, and
custom drivers plug in through
Wizard::extend(). - Opt-in JSON API.
Route::wizard('order', OrderWizard::class)registers resource-style routes with one response shape. The package registers no routes by itself. - Generators.
php artisan wizard:makeandwizard:make-stepare built on Laravel'sGeneratorCommand.wizard:make-step --viewalso creates the step's Blade view withmake:view. - Events.
WizardStarted,StepCompleted,StepSkipped,StepReopened,WizardCompleted,WizardReset. - Translated messages in English and Slovak.
Requirements
- PHP 8.4 or higher
- Laravel 12 or 13
Installation
composer require invelity/laravel-headless-wizard
The session store works out of the box. To keep state in the database instead:
php artisan vendor:publish --tag=wizard-migrations php artisan migrate
Then set WIZARD_STORE=database.
Quick start
Generate a wizard and its steps:
php artisan wizard:make OrderWizard php artisan wizard:make-step CalculatorStep --wizard=OrderWizard php artisan wizard:make-step PersonalDataStep --wizard=OrderWizard php artisan wizard:make-step ConfirmationStep --wizard=OrderWizard --display
The wizard lists its steps in order:
namespace App\Wizards; use App\Wizards\Steps\CalculatorStep; use App\Wizards\Steps\ConfirmationStep; use App\Wizards\Steps\PersonalDataStep; use Invelity\WizardPackage\Wizard; class OrderWizard extends Wizard { protected array $steps = [ CalculatorStep::class, PersonalDataStep::class, ConfirmationStep::class, ]; }
Each step names the form request that validates it, and may transform the data it stores:
namespace App\Wizards\Steps; use App\Http\Requests\Wizards\CalculatorRequest; use App\Services\PriceCalculator; use Invelity\WizardPackage\Step; class CalculatorStep extends Step { protected ?string $formRequest = CalculatorRequest::class; public function handle(array $data, PriceCalculator $prices): array { return [...$data, 'price' => $prices->quote($data['weight'])]; } }
Drive it from your own controllers…
Route::get('/order/{step}', [OrderController::class, 'show']) ->middleware('wizard.step:'.OrderWizard::class) // redirects to the step the visitor should be on ->name('order.step'); Route::post('/order/{step}', [OrderController::class, 'store']);
…or expose the JSON API for a single-page or mobile frontend:
Route::middleware('web')->group(function () { Route::wizard('order', OrderWizard::class); });
Documentation
The full documentation lives at invelity.github.io/laravel-headless-wizard:
Upgrading from 1.x? Read UPGRADING.md.
Testing
composer test
composer lint
Contributing and security
See CONTRIBUTING.md. Please report security issues as described in SECURITY.md, not in public issues.
License
The MIT License (MIT). See LICENSE.md.
