guava/onboarding
Guava Onboarding provides a Laravel-friendly foundation for building user onboarding flows, with structured steps and a simple API to guide new users through setup. Add configurable onboarding journeys to your app without building the plumbing from scratch.
Install via Composer and publish the package assets:
composer require guava/onboarding
php artisan vendor:publish --tag="onboarding-migrations"
php artisan migrate
The first place to look is the HasOnboarding trait — this is what plugs the package into any Eloquent model (typically User, but also Team or Company). Add it to the model that should go through onboarding:
use Guava\Onboarding\Concerns\HasOnboarding;
class User extends Authenticatable
{
use HasOnboarding;
}
Next, define your first step. A step is any class implementing the OnboardingStep contract. The trait gives you $user->onboardings(), $user->onboardingSteps(), and helpers like $user->isOnboarded() / $user->markStepAsCompleted(...). Your first realistic use case is a "welcome checklist" for new signups: define 3–5 steps (e.g. VerifyEmailStep, CreateFirstProjectStep, InviteTeammateStep), render them in a page, and gate each step's action behind the step's action() callback. If you also need an admin-facing onboarding builder, the adjacent Filament plugin is the second thing to install — see the Filament notes below.
Step classes as the primary unit of work. Keep each onboarding step in app/Onboarding/Steps/. A step defines key(), title(), description(), action() (the URL or callback that advances it), and optionally condition() and dependencies(). Treating steps as classes (not array config) is what makes them testable and lets you inject services via the container.
Dependency chaining. Use dependencies() to declare ordering without hardcoding a sequence. This lets you render the same step list in multiple places (dashboard card, dedicated onboarding page, notification) and have the package resolve what's actionable. Prefer this over a manually curated sort column — the dependency graph is what powers nextStep().
Programmatic completion. For server-side events (webhook received, first API call), call $user->completeOnboardingStep(VerifyEmailStep::class) inside the relevant event listener rather than relying on the UI button. This keeps onboarding state consistent whether the user clicks through or the system advances them.
Filament integration. Register the plugin in your panel provider (->plugin(OnboardingPlugin::make())). Since 2.2.1 is the version that fixes Filament v5 compatibility, pin guava/onboarding to ^2.2 if your panel is on Filament v5 — earlier 2.2.0 installs can break on the resource registration. The plugin exposes an admin UI for tracking completion rates, which is genuinely useful for product teams; use it read-only unless you deliberately want admins to complete steps on behalf of users.
Scoping to multiple entities. Because the trait is model-agnostic, a common pattern is to apply onboarding to both User and Team. Steps should either be entity-agnostic or accept the entity type in condition() so you don't force the same checklist on both.
onboardable reference. If you're onboarding both User and Team, verify the polymorphic index exists before relying on morphTo queries at scale.condition() is evaluated at render time, not cached. Don't put expensive queries in a condition — it runs on every step-list render. Memoize on the model instance if needed.completeOnboardingStep() twice creates duplicate completion rows unless you guard with a wasCompleted() check or the once config. Wrap completion in a small domain service if the triggering action can fire twice (retries, double-clicks, webhook replays).$user->onboardingSteps() returns the resolved step objects with their current completion state — dump this when a step "isn't showing." The usual culprits are a failing condition(), an unmet dependencies() entry, or a step key collision across flows.php artisan vendor:publish --tag="onboarding-config") to override the storage model and table names before adding your own fields; extending the completion model after migration is painful. If you need custom persistence (e.g. append-only event sourcing), subclass the provided model rather than reimplementing the trait.completeOnboardingStep(StepClass::class).How can I help you explore Laravel packages today?