Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Onboard Laravel Package

calebporzio/onboard

Onboard is a small Laravel package for adding simple, customizable onboarding flows to your app. Define steps with familiar Blade views, guide new users through them, and track completion so you can send them into your app when they're ready.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. calebporzio/onboard is a micro-package that adds an Onboardable trait to any Eloquent model and persists onboarding progress in two columns on the model itself: a JSON onboarded_steps array and an onboarded_at timestamp. That maps cleanly onto a single well-understood product concern — the post-registration activation flow — with no new tables, no service layer, and no queue or event infrastructure. As a TPM, the value here is less the code and more the forcing function: adopting it forces the team to name and order the steps that constitute "a successful new user," which is the precondition for any activation metric we might promise a stakeholder.

Integration feasibility. Low effort, high clarity. Install, publish the migration, migrate, add the trait to User, and define steps. The public surface is small enough to audit in under an hour (Onboardable.php, the published migration, any published config). Two integration realities to budget for: the README's method names disagree with community write-ups (markAsComplete/isComplete vs markStepAsComplete/hasCompletedStep/isOnboarded), so verification in tinker is a required task, not a nice-to-have; and the package validates nothing — a typo'd step key succeeds silently and can leave isOnboarded() reachable without the real step completed. Both are absorbed by a thin application-owned service (a config/onboarding.php step registry plus an OnboardingProgress class with an abort_unless guard), which is where I would scope the work regardless of vendor.

Technical risk. The dominant risk is lifecycle, not code. The repository is archived, the last release (v1.7, Sept 2020) added Laravel 8 support and nothing else, there are zero dependents, and there is no maintainer response path. Laravel 8 itself is end-of-life, so the compatibility ceiling is a version we should be migrating off of, not toward. Secondary risks are behavioral and well understood: the JSON column is read-modify-write, so concurrent completions of different steps on the same user can clobber each other; JSON-cast staleness means a User hydrated before a write will report stale progress; isOnboarded() and the step list are decoupled, so adding a fifth step later does not un-onboard already-completed cohorts; and the columns must stay out of $fillable or users can mass-assign themselves to activated. Trait method collisions on User are fatal in PHP. None of these are unknown-unknowns — they are documented and cheap to mitigate — but they are ours to mitigate forever.

Key questions.

  • Are we on Laravel 8 or earlier today, and what is the committed date for the framework upgrade? That date effectively sets this package's expiration.
  • What are the exact steps, in order, and what is the definition of "onboarded"? Who owns that copy and that definition — product or growth?
  • Is this a throwaway prototype in a spike branch, or a production dependency on a customer-facing surface? The answer changes the recommendation from "install it today" to "copy the trait and migration into app/ and own it" — roughly one to two developer days.
  • Does anything else in the dependency graph already define isOnboarded() or similar generic methods on User?
  • Do queued jobs or background listeners mark steps? If yes, we need transaction-plus-row-lock on the write, which is scope the package does not provide.
  • Is onboarding expected to change version to version (new steps per cohort)? If so, a flow_version key or our own column is required, because onboarded_at will otherwise mean two different things across cohorts.

Integration Approach

Stack fit. Fits any Laravel application with an authenticatable Eloquent model and a standard users table migration path. It is framework-native — trait, migration, cast — with no external services, no additional infrastructure, and no runtime dependencies beyond Laravel itself. That is precisely why the make-versus-buy answer is short.

Migration path. Treat the package as a design spec, not a long-lived dependency. Two viable paths:

  1. Prototype path (same sprint): composer require calebporzio/onboard, publish and run the migration, add the trait, and wire a checklist. Appropriate only on a Laravel 8-era branch, only for validating copy and step ordering with real users, and only with an explicit agreement that it does not ship to production.
  2. Production path (recommended): copy the trait and migration into app/ and own them, or write the equivalent as a user_onboarding_steps table keyed by user, step, and completed timestamp. The package's own schema is a fine starting point; the table form removes the read-modify-write concurrency hazard and makes funnel queries tractable, which the JSON array does not.

Either way, build the application-owned seam first: a canonical config/onboarding.php registry (label, route, ordering) as the single source of truth, and an OnboardingProgress service that all trait calls funnel through. That seam gives us validation the package lacks, one place to emit analytics events the package does not emit, and a one-file swap if we later replace the storage.

Compatibility. The hard constraint is Laravel ≤ 8. On Laravel 9+ the Composer constraints fail even though the underlying code is trivially compatible — which is an argument for copying the trait rather than fighting the constraint. Verify method names against the installed version before any UI work depends on them. Keep the two onboarding columns out of $fillable.

Sequencing.

  1. Name and order the steps; get product sign-off on the definition of "onboarded" and on step copy. This is the deliverable that outlives the package.
  2. Decide prototype versus production path, and record the decision with its expiry condition (e.g., "revisit at framework upgrade").
  3. Build the config registry and the OnboardingProgress service with key validation.
  4. Add an EnsureOnboarded middleware on the authenticated route group with an allowlist for onboarding.* routes — one gate, not per-controller checks.
  5. Mark steps from domain-event listeners (EmailVerified, ProjectCreated, TeamMemberInvited) rather than controllers, so steps complete through API and console paths too.
  6. Render the checklist from config (checkmark per step, "Next: …" CTA on the first incomplete step, progress bar from the service).
  7. Handle the completion transition explicitly: stamp onboarded_at, fire UserOnboarded, notify, clear any feature-flag override.
  8. Ship reset and replay tooling for support and QA, plus an onboarded() factory state, before the first support ticket arrives.

Operational Impact

Maintenance. Effectively zero vendor maintenance and non-zero ownership cost. The package is archived with no fix path, so any defect is ours; the mitigation is to keep all extension logic in our own service and never edit vendor. The realistic annual burden is small — the surface is one migration and one trait — but it is a permanent line item as long as the dependency exists, and it grows a tail the moment we need step versioning or replay, which the package does not support. Ownership cost of the in-house equivalent is estimated at one to two developer days with near-zero ongoing maintenance, which is the cleanest argument against adopting.

Support. No maintainer, no issue triage, no security advisories. Internal support load is real but bounded and diagnosable: most "stuck onboarding" tickets reduce to a typo'd step key, a stale model instance, or a step marked from a code path that never executed. The diagnostic is one query — compare getRawOriginal('onboarded_steps') against config('onboarding.steps'). That runbook should be written at launch, not after the first escalation, and paired with reset/replay tooling so support does not need engineering to unstick a user.

Scaling. The data model scales to per-user checks and does not scale to analysis. A JSON array per user is not a funnel — cross-cohort queries ("what share of signups complete step 3 within 7 days?") are painful, especially on SQLite, and the package emits no events. If growth needs activation reporting, we must emit OnboardingStepCompleted and UserOnboarded ourselves and ship them to PostHog, Amplitude, or Mixpanel. Write concurrency is the other scaling edge: read-modify-write on the JSON column means parallel step completions can lose writes. For a human-driven checklist that is tolerable (the user retriggers); for queued jobs it requires a transaction with a row lock, which is scope the package does not carry.

Failure modes.

  • Silent invalid step keys — no validation; a typo creates a phantom step and can let isOnboarded() be reached without the real step done. Mitigation: config registry plus hard guard in the service.
  • Lost writes — concurrent completions clobber the JSON array. Mitigation: row-lock the write, or accept eventual re-check for UI-driven flows.
  • Stale reads — fresh()/refresh() required after any write in a different object graph (tests, queued listeners, Livewire components).
  • Trait collision — another package defining isOnboarded() fatals PHP. Mitigation: alias the trait or rename methods.
  • Cohort drift — adding steps post-launch does not un-onboard existing users; "onboarded" silently means different things across cohorts. Mitigation: version the flow deliberately.
  • Mass-assignment escalation — columns in $fillable let users mark themselves activated. Mitigation: exclude them and write only through the service.
  • Framework upgrade wall — the next Laravel major makes Composer install fail. Mitigation: keep an exit plan (a user_onboarding_steps table) so removal is a backfill plus a delete, not a rewrite.

Ramp-up. Low. Engineering: hours to first working checklist, a day to the hardened version with registry, service, middleware, and events. Product: the real time is spent naming steps and agreeing on the definition of activation — a half-day workshop, and the highest-value output of the entire exercise. Support: one runbook page and the reset tooling. Stakeholders should hear the recommendation plainly: the capability is worth building, the package is not worth depending on. Use the repo as the spec, build the one-to-two-day in-house equivalent, and keep the analytics, lifecycle messaging, and branching-flow questions as separate product decisions with separate tooling rather than letting a checklist project quietly absorb them.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle