spatie/laravel-onboard
Define and track user onboarding steps in Laravel. Register steps with titles, links, CTAs, and completion rules, then query a user’s onboarding progress (in progress/completed) and render a customizable checklist in your views.
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 (e.g., AppServiceProvider@boot):
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:
@if(auth()->user()->onboarding()->inProgress())
<div class="onboarding-progress">
@foreach(auth()->user()->onboarding()->steps as $step)
<div class="step {{ $step->complete() ? 'complete' : 'incomplete' }}">
{{ $step->title }}
</div>
@endforeach
</div>
@endif
Step Definition:
Onboard::addStep() to define steps with:
title(): Human-readable name.link(): URL to navigate when clicked.cta(): Call-to-action text (e.g., "Start").completeIf(): Closure to check completion (e.g., fn ($user) => $user->posts->count() > 0).Onboard::addStep('Verify Email')
->link('/email/verify')
->cta('Verify')
->completeIf(fn ($user) => $user->hasVerifiedEmail());
Dynamic Completion Logic:
->completeIf(fn ($user) => $user->roles()->where('name', 'admin')->exists());
Rendering Steps:
@foreach(auth()->user()->onboarding()->steps as $step)
<div class="progress-step {{ $step->complete() ? 'done' : 'pending' }}">
{{ $step->title }}
</div>
@endforeach
document.querySelector('.onboard-step').addEventListener('click', (e) => {
window.location.href = e.target.dataset.link;
});
Conditional Steps:
Onboard::addStep('Set Preferences')
->visibleIf(fn ($user) => $user->preferences->isEmpty())
->completeIf(fn ($user) => !$user->preferences->isEmpty());
Integration with Auth Events:
use Spatie\Onboard\Events\UserOnboardingReset;
User::updated(function ($user) {
event(new UserOnboardingReset($user));
});
Migration Conflicts:
onboard_steps table migration doesn’t conflict with existing tables. Run:
php artisan migrate:fresh --seed
if testing locally.Closure Scope:
completeIf()/visibleIf() lose scope. Use use ($user) or arrow functions:
->completeIf(fn () => auth()->user()->hasVerifiedEmail());
Caching Issues:
php artisan onboard:clear-cache
Step Order:
Onboard::reorderSteps([3, 1, 2]); // Reorders steps by their IDs
Testing:
Onboard facade in tests:
$this->partialMock(Onboard::class, function ($mock) {
$mock->shouldReceive('steps')->andReturn([...]);
});
Log Step Completion: Add a temporary check to debug why a step isn’t completing:
->completeIf(fn ($user) => {
\Log::info('Checking completion for', ['user_id' => $user->id, 'step' => 'Complete Profile']);
return $user->profile->isComplete();
});
Inspect User Onboarding: Dump the user’s onboarding state in Tinker:
php artisan tinker
>>> $user = App\User::first();
>>> $user->onboarding()->steps
Custom Step Models:
Extend the Spatie\Onboard\Models\OnboardStep model to add custom fields:
php artisan make:model OnboardStepExtension --extends=Spatie\Onboard\Models\OnboardStep
Event Listeners:
Listen for onboarding events (e.g., StepCompleted):
Onboard::stepCompleted(function ($user, $step) {
// Send notification or log completion
});
API Endpoints: Expose onboarding progress via API:
Route::get('/api/onboarding', function () {
return auth()->user()->onboarding()->steps;
});
Localization: Localize step titles/CTAs by publishing the language files:
php artisan vendor:publish --provider="Spatie\Onboard\OnboardServiceProvider" --tag="lang"
How can I help you explore Laravel packages today?