spatie/laravel-livewire-onboard
Define and track user onboarding steps in Laravel. Register steps with links, CTAs, and completion rules, then check progress per user and render the flow in Blade. Helps show what’s done, what’s next, and when onboarding is complete.
Installation:
composer require spatie/laravel-onboard
Publish the migration:
php artisan vendor:publish --provider="Spatie\Onboard\OnboardServiceProvider" --tag="migrations"
Run migrations:
php artisan migrate
Define Onboarding Steps:
Configure steps in a service provider's boot() method (e.g., AppServiceProvider):
use Spatie\Onboard\Facades\Onboard;
Onboard::addStep('Complete Profile')
->link('/profile')
->cta('Complete')
->completeIf(fn ($user) => $user->profile->isComplete());
First Use Case: Display the onboarding progress in a Blade template:
@auth
@if(auth()->user()->onboarding()->inProgress())
<div class="onboarding-progress">
@foreach(auth()->user()->onboarding()->steps as $step)
<div class="{{ $step->complete() ? 'complete' : 'pending' }}">
{{ $step->title }}
</div>
@endforeach
</div>
@endif
@endauth
Step Definition:
Onboard::addStep() in a centralized location (e.g., AppServiceProvider).Onboard::addStep('Verify Email')
->link('/email/verify')
->cta('Verify Now')
->completeIf(fn ($user) => $user->hasVerifiedEmail());
Dynamic Step Logic:
->completeIf(fn ($user) => $user->roles()->where('name', 'admin')->exists())
Livewire Integration:
public function completeStep($stepId) {
$user = auth()->user();
$user->onboarding()->completeStep($stepId);
$this->emit('onboardingUpdated');
}
<div wire:ignore>
@foreach($user->onboarding()->steps as $step)
<button wire:click="completeStep({{ $step->id }})">
{{ $step->cta }}
</button>
@endforeach
</div>
Middleware for Onboarding Gating:
Route::middleware(['auth', 'onboarding'])->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
});
public function handle(Request $request, Closure $next) {
if (auth()->user()->onboarding()->inProgress()) {
return redirect()->route('onboarding.index');
}
return $next($request);
}
Migration Conflicts:
onboard_steps table migration doesn’t conflict with existing tables. Run:
php artisan migrate --pretend
to verify before applying.Closure Scope:
completeIf closures. Use explicit dependencies:
->completeIf(fn ($user) => $user->profile->isComplete())
instead of relying on global state.Livewire State Sync:
$this->dispatch('refreshOnboarding');
and listen in the component:
protected $listeners = ['refreshOnboarding' => '$refresh'];
Caching Quirks:
php artisan config:clear
Inspect Steps:
dd(auth()->user()->onboarding()->steps->toArray());
Check Completion Logic:
completeIf conditions in isolation:
$user = auth()->user();
dd($user->profile->isComplete()); // Verify logic
Middleware Debugging:
Route::middleware(['auth'])->group(function () { ... });
Custom Step Models:
Spatie\Onboard\Models\Step model for additional fields:
php artisan make:model OnboardingStep --extends="Spatie\Onboard\Models\Step"
Event Listeners:
Onboard::completed(function ($user, $step) {
// Send notification, log analytics, etc.
});
API Integration:
Route::get('/api/onboarding', function () {
return auth()->user()->onboarding()->steps;
});
How can I help you explore Laravel packages today?