wallacemartinss/filament-onboarding
Database-driven onboarding for Filament v5: a progress checklist that follows users across every panel page, guided spotlight tours, video steps, and condition-based steps that complete automatically so existing users enter journeys already partly done.
Confirm which line you are on — the version constraint changed. This package now ships two parallel lines that share a source tree and differ only in Composer constraints and Livewire internals:
Earlier guidance said “Filament v5 only” — that is no longer true and was already misleading for the 1.x line. Check composer.json for the exact filament/filament constraint and pick the matching major of this package before requiring it. Getting this wrong is a Composer resolution fight, not a runtime warning.
Install and publish:
composer require wallacemartinss/filament-onboarding
php artisan vendor:publish --tag="filament-onboarding-config"
php artisan vendor:publish --tag="filament-onboarding-migrations"
php artisan migrate
php artisan filament:assets
The filament:assets call is new to v1.2.0 and is not optional: the tour runner changed in this release, and an un-republished asset bundle leaves you running old runner JS against new definitions. Treat it as part of both install and every upgrade. Publish tags and the plugin FQCN are still worth verifying against the README for the release you install; the package is young enough that install ergonomics may change between minors.
Register the plugin on the panel that should show onboarding:
use Wallacemartinss\FilamentOnboarding\FilamentOnboardingPlugin;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->plugin(FilamentOnboardingPlugin::make());
}
Where to look first:
config/filament-onboarding.php — audience rules, render hooks, tour settings, translation behavior.journeys, steps, progress/completion tables) before writing any reporting or backfill jobs.src/Models — the models you will query for progress dashboards and support tooling.First use case to validate the package: create a single “Getting started” journey with three step types:
profile_completed.Then log in as a test user and confirm the checklist follows you across pages, the condition step self-completes for a user whose profile was already complete, and the tour survives a page navigation — including a tour that crosses to another page and resumes, which is exactly the path v1.2.0 fixed (advance controls are now pressed when a stop is arrived at rather than walked to).
If you are on a multi-tenant panel, add a fourth check: a step pointing at the tenant profile page. As of v1.2.0 ->tenantProfile() destinations appear in the step destination picker; before this release they were silently absent because getPages() never listed them.
Author in the database, seed in code. Product people should edit journeys in the panel, but environments still need reproducible onboarding. Write a seeder using updateOrCreate keyed by a stable slug so staging and production get the same starting journey without duplicating rows on every deploy. Never truncate onboarding tables in a deploy script — you will wipe real user progress.
Register conditions as named, testable units. The condition callback is the heart of the package: it lets existing users enter a journey already half-done instead of being nagged about work they completed years ago. Keep condition keys stable and centralize them in one service provider or dedicated registry class:
// Shape of the integration; adapt the facade/service name to the package API.
Onboarding::condition('profile_completed', fn (User $user) =>
$user->profile()->whereNotNull('completed_at')->exists()
);
Onboarding::condition('has_created_invoice', fn (User $user) =>
$user->invoices()->exists()
);
Write a test that asserts every key referenced by seeded steps exists in the registry. A typo in a condition key fails silently — the step simply never completes — and that is the single most likely support ticket you will receive.
Use the right step type for the job. Conditions are for state that exists independent of navigation (“has created a record”, “has connected Stripe”). Page-reached steps are for exposure (“opened the Reports page”). Video steps are for content consumption. Do not model “created your first invoice” as a page-reached step; users will hit the page, fail to create anything, and still get credit.
Treat tours as fragile UI contracts. Spotlight tours depend on selectors resolving to visible elements. Prefer stable Filament component IDs or your own data-onboarding="..." attributes over generated CSS classes. Add those attributes deliberately to the buttons and panels you want to highlight. Re-test tours after every Filament minor upgrade; a selector that resolves to null typically produces a tour with a missing step or a misplaced spotlight.
Target fields inside nested layouts — this now works, but it is worth an explicit test. v1.2.0 fixed the case where a field’s name (title) and its state path (form.details.title) diverged because a Group, Section, Tab or wizard step prefixed it. The matcher now accepts the name at the end of the path as well as the whole of it. That fix also means: build at least one tour against your most deeply nested form (a wizard inside a section inside a tab) and confirm it targets and completes. Older versions stalled on Next with no recovery short of Skip, and the stall was persisted to localStorage, so it recurred on every reload.
Do not fight a target picker that looks empty. If a form resource offers no targets at all, and it calls anything that reaches the Livewire component from inside the schema, you were hitting the Schema::getLivewire() TypeError that v1.2.0 fixed by building the schema against the resource’s own page. On the reporting panel this was measured against, 8 of 34 form resources were blank for this reason. On ≥1.2.0 they should populate; if one still doesn’t, that is a bug report, not a workaround.
Scope the checklist per panel and tenant. In multi-panel apps, register the plugin only where onboarding belongs, or configure the audience per panel. In multi-tenant apps, add tenant identity to the audience/condition logic and verify that progress rows cannot leak across tenants. This is the area where a generic “show checklist to authenticated user” default becomes a security-adjacent bug.
Localize content at the journey level, not in lang files. The selling point is that translators can edit step titles, descriptions, and tour copy in the database. Decide early whether you store translations as JSON columns or per-locale step rows, configure a sensible fallback locale, and test with a user whose locale differs from app.locale. Keep an export/import path (JSON seed file or command) so translations are versioned in Git rather than living only in production.
Wire completion events into the rest of the app. When a step or journey completes, use the package’s events/listeners to update your CRM, send a “you’re set up” email, or record analytics. Guard listeners for idempotency — a re-evaluated condition or a page revisit should not double-count a conversion.
Expose progress to support and success teams. Query the package models directly for a small “onboarding status” column on your Users resource, or build a lightweight progress page. This turns the package from a UI widget into an operational tool.
php artisan filament:assets on install and on every upgrade of this package. v1.2.0 changed the tour runner; a stale asset bundle is the kind of failure that looks like a logic bug in a step you have already configured correctly.updateOrCreate, and verify how versioning a journey affects existing progress.queue:work the manager object persists, so a worker kept serving a flow the author had switched off, because the cache-clearing write fires its model events in whichever process handled it. v1.2.0 ties the memo to the worker’s actual request/job boundaries. This is a correctness fix you get for free, not a reason to stop restarting queue workers after a deploy — and php artisan optimize:clear after config changes remains good practice.How can I help you explore Laravel packages today?