PHPackages                             dgtlinf/user-onboarding - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. dgtlinf/user-onboarding

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

dgtlinf/user-onboarding
=======================

A lightweight, stateless user onboarding flow manager for Laravel applications.

v1.1.1(7mo ago)14MITPHPPHP ^8.2CI passing

Since Oct 13Pushed 7mo agoCompare

[ Source](https://github.com/dgtlinf/user-onboarding)[ Packagist](https://packagist.org/packages/dgtlinf/user-onboarding)[ RSS](/packages/dgtlinf-user-onboarding/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (4)Dependencies (5)Versions (5)Used By (0)

User Onboarding for Laravel 10+
===============================

[](#user-onboarding-for-laravel-10)

[![Latest Version on Packagist](https://camo.githubusercontent.com/25bc1002e85dc34ecc06e279c7e3b97e9ec1aaf81d89394c8f9ae05a08d15e86/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6467746c696e662f757365722d6f6e626f617264696e672e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/dgtlinf/user-onboarding)[![GitHub Tests Action Status](https://camo.githubusercontent.com/eb617013ef18ba16490807e71069baa11e0a9465b41bb28e4fd874404bd68eab/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6467746c696e662f757365722d6f6e626f617264696e672f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/dgtlinf/user-onboarding/actions)[![Total Downloads](https://camo.githubusercontent.com/bf4dd6d8123800cf550cb76c19708ca70b283e0b9a0608acf1b0b15263e5fbd3/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6467746c696e662f757365722d6f6e626f617264696e672e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/dgtlinf/user-onboarding)[![License](https://camo.githubusercontent.com/20ad941a55cc6036a55a177b7873a76ae668e1d64fc12997798d87b5f5f2dcb3/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f6467746c696e662f757365722d6f6e626f617264696e672e7376673f7374796c653d666c61742d737175617265)](LICENSE.md)[![PHP Version](https://camo.githubusercontent.com/7cb6e51c03bd8f9bac7dad425741162bd74d0ce060270964054394ffad785eca/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d253545382e322d626c75653f7374796c653d666c61742d737175617265)](https://camo.githubusercontent.com/7cb6e51c03bd8f9bac7dad425741162bd74d0ce060270964054394ffad785eca/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d253545382e322d626c75653f7374796c653d666c61742d737175617265)[![Laravel Version](https://camo.githubusercontent.com/6eb28b6d03f846d8178cf008fe1f1572e186e6f2af8dbd7e5498eb285e77d97a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c61726176656c2d25354531302d6f72616e67653f7374796c653d666c61742d737175617265)](https://camo.githubusercontent.com/6eb28b6d03f846d8178cf008fe1f1572e186e6f2af8dbd7e5498eb285e77d97a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c61726176656c2d25354531302d6f72616e67653f7374796c653d666c61742d737175617265)

A lightweight, stateless **user onboarding flow manager** for Laravel applications. Define onboarding steps entirely in code or in configuration, use middleware to restrict access, and listen to onboarding events — all without a database.

---

🚀 Installation
--------------

[](#-installation)

Install the package via Composer:

```
composer require dgtlinf/user-onboarding
```

Then publish the config file and installation assets using the included install command:

```
php artisan user-onboarding:install
```

This will publish the configuration file:

```
config/user-onboarding.php

```

---

⚙️ Defining Flows
-----------------

[](#️-defining-flows)

You can define your onboarding steps in **two different ways** — depending on whether your flow is static (defined in `config`) or dynamic (defined programmatically).

### 1. Config-Based Flows (Recommended)

[](#1-config-based-flows-recommended)

This is the most common approach. Define your flows in `config/user-onboarding.php`:

```
use Dgtlinf\UserOnboarding\Step;

return [
    'flows' => [
        'default' => [
            Step::make('profile')->check(fn($user) => filled($user->name)),
            Step::make('verify_email')->check(fn($user) => $user->hasVerifiedEmail()),
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Onboarding Redirects
    |--------------------------------------------------------------------------
    |
    | Define where users should be redirected when onboarding is incomplete
    | for a specific flow. The 'default' route is used if none match.
    |
    */
    'redirects' => [
        'default' => '/onboarding',
    ],
];
```

The middleware and facade will automatically use these definitions:

```
$flow = UserOnboarding::start($user, 'default');
```

### 2. Dynamic Flows (Programmatic)

[](#2-dynamic-flows-programmatic)

You can also define steps directly in code — perfect for custom setups, role-based flows, or feature flags.

#### In a Controller

[](#in-a-controller)

```
use Dgtlinf\UserOnboarding\Facades\UserOnboarding;
use Dgtlinf\UserOnboarding\Step;

public function onboarding()
{
    $user = auth()->user();

    $flow = UserOnboarding::for($user)
        ->addStep(Step::make('fill_profile')->check(fn($u) => filled($u->name)))
        ->addStep(Step::make('upload_avatar')->check(fn($u) => $u->avatar !== null));

    return inertia('Onboarding/Index', [
        'steps' => $flow->steps(),
        'currentStep' => $flow->current()?->slug,
        'progress' => $flow->progress(),
    ]);
}
```

#### In AppServiceProvider (Global Logic)

[](#in-appserviceprovider-global-logic)

You can register a reusable macro for dynamic onboarding flows:

```
use Dgtlinf\UserOnboarding\Facades\UserOnboarding;
use Dgtlinf\UserOnboarding\Step;

public function boot()
{
    UserOnboarding::macro('dynamicFlow', function ($user) {
        $flow = UserOnboarding::for($user)
            ->addStep(Step::make('profile')->check(fn($u) => filled($u->name)))
            ->addStep(Step::make('verify_email')->check(fn($u) => $u->hasVerifiedEmail()));

        if ($user->is_team_owner) {
            $flow->addStep(Step::make('invite_members')->check(fn($u) => $u->team->members->count() > 1));
        }

        return $flow;
    });
}
```

Now anywhere in your app, you can call:

```
UserOnboarding::dynamicFlow($user)->progress();
```

---

🧬 Basic Usage
-------------

[](#-basic-usage)

```
use Dgtlinf\UserOnboarding\Facades\UserOnboarding;
use Dgtlinf\UserOnboarding\Step;

$user = auth()->user();

$flow = UserOnboarding::for($user)
    ->addStep(Step::make('profile')->check(fn($u) => $u->profileCompleted()))
    ->addStep(Step::make('verify_email')->check(fn($u) => $u->hasVerifiedEmail()));

if ($flow->isCompleted()) {
    // continue to dashboard
}

$flow->progress(); // e.g. 50.0
```

If you prefer configuration-based flows, use:

```
$flow = UserOnboarding::start($user, 'default');
```

---

🛡️ Middleware Protection
------------------------

[](#️-middleware-protection)

You can easily protect routes to ensure users can only access them after completing onboarding.

### Protect Entire Routes

[](#protect-entire-routes)

```
Route::middleware('onboarding.step')->group(function () {
    Route::get('/dashboard', DashboardController::class);
});
```

If a user has not completed onboarding, they’ll be redirected to `/onboarding` (or your configured path).

### Require a Specific Step

[](#require-a-specific-step)

```
Route::middleware('onboarding.step:verify_email')->group(function () {
    Route::get('/projects', ProjectsController::class);
});
```

If the user hasn’t completed the `verify_email` step, the middleware denies access.

---

🤓 Creating a Custom Middleware
------------------------------

[](#-creating-a-custom-middleware)

By default, this package includes the middleware `Dgtlinf\\UserOnboarding\\Http\\Middleware\\EnsureUserOnboardingStepCompleted`.

You can register it manually or create your own to customize the behavior (for example, JSON vs. redirect responses).

### 1. Using the built-in middleware

[](#1-using-the-built-in-middleware)

Register it in your `app/Http/Kernel.php` if not already auto-discovered:

```
protected $routeMiddleware = [
    'onboarding.step' => \\Dgtlinf\\UserOnboarding\\Http\\Middleware\\EnsureUserOnboardingStepCompleted::class,
];
```

Then use it in your routes:

```
Route::middleware('onboarding.step')->group(function () {
    Route::get('/dashboard', DashboardController::class);
});
```

### 2. Creating your own middleware

[](#2-creating-your-own-middleware)

If you need different behavior (e.g. API response instead of redirect), you can create your own middleware and use the package’s API directly:

```
php artisan make:middleware EnsureOnboardingForApi
```

```
namespace App\\Http\\Middleware;

use Closure;
use Dgtlinf\\UserOnboarding\\Facades\\UserOnboarding;

class EnsureOnboardingForApi
{
    public function handle($request, Closure $next)
    {
        $user = $request->user();

        // Use a specific flow if needed
        $flow = UserOnboarding::start($user, 'default');

        if (! $flow->isCompleted()) {
            // For APIs, return a JSON response instead of redirect
            return response()->json([
                'message' => 'User onboarding not completed',
                'next_step' => $flow->current()?->slug,
                'progress' => $flow->progress(),
            ], 403);
        }

        return $next($request);
    }
}
```

Then register and use it:

```
Route::middleware('onboarding.api')->get('/api/profile', [ProfileController::class, 'show']);
```

✅ **Tip:**You can also use this pattern to create per-role or per-guard middleware variants:

```
UserOnboarding::start($user, $user->isAdmin() ? 'admin' : 'default');
```

---

🧬 Example Workflow (Blade or Inertia)
-------------------------------------

[](#-example-workflow-blade-or-inertia)

When a user tries to access a protected route, the middleware redirects them to `/onboarding`. You can use the flow object to determine which step to render next.

### Controller Example

[](#controller-example)

```
use Dgtlinf\UserOnboarding\Facades\UserOnboarding;

public function show()
{
    $user = auth()->user();

    $flow = UserOnboarding::start($user, 'default');

    return inertia('Onboarding/Index', [
        'currentStep' => $flow->current()?->slug,
        'steps' => $flow->steps()->map(fn($s) => [
            'slug' => $s->slug,
            'completed' => $flow->isStepCompleted($s->slug),
        ])->values(),
        'progress' => $flow->progress(),
    ]);
}
```

### Inertia/Vue Example

[](#inertiavue-example)

```

        Onboarding Progress

            {{ step.slug }}

    defineProps({ currentStep: String, steps: Array, progress: Number })

    function getStepComponent(slug) {
        switch (slug) {
            case 'profile':
                return 'OnboardingProfileStep'
            case 'verify_email':
                return 'OnboardingVerifyEmailStep'
            default:
                return 'OnboardingDone'
        }
    }

```

### Blade Example

[](#blade-example)

```
@php
    $flow = UserOnboarding::start(auth()->user());
    $current = $flow->current()?->slug;
@endphp

@if ($current === 'profile')
    @include('onboarding.steps.profile')
@elseif ($current === 'verify_email')
    @include('onboarding.steps.verify-email')
@else
    All done! 🎉
@endif
```

When a user completes a step (e.g., submits a form):

```
UserOnboarding::for($user)->completeStep('profile');
```

The next time the onboarding view loads, the next step will automatically render.

---

🧱 Listening to Events
---------------------

[](#-listening-to-events)

The package dispatches the following events automatically:

EventDescription`OnboardingStarted`Fired when a flow begins via `UserOnboarding::start()``StepCompleted`Fired when a step is completed manually or programmatically`OnboardingCompleted`Fired when all steps are completed### Example Listener

[](#example-listener)

```
use Dgtlinf\UserOnboarding\Events\StepCompleted;

Event::listen(StepCompleted::class, function ($event) {
    Log::info('Step completed', [
        'user' => $event->user->id,
        'step' => $event->step->slug,
    ]);
});
```

---

📡 Events Overview
-----------------

[](#-events-overview)

- `OnboardingStarted` → emitted when a user begins onboarding
- `StepCompleted` → emitted when a user finishes a step
- `OnboardingCompleted` → emitted when a flow is fully done

Each event carries the `$user` and `$flow` (and `$step` when relevant).

---

🥉 Example Use Cases
-------------------

[](#-example-use-cases)

- Block certain routes until user setup is finished
- Show onboarding progress bar in the UI
- Trigger reminders via queued listeners
- Log onboarding analytics and completions
- Connect with external CRM or email campaigns

---

⚙️ Publishing &amp; Customization
---------------------------------

[](#️-publishing--customization)

You can re-publish configuration anytime:

```
php artisan vendor:publish --tag="user-onboarding-config"
```

---

🧮 Tech Notes
------------

[](#-tech-notes)

- **Stateless**: No database persistence — each step is evaluated live.
- **Extensible**: Add events, listeners, and custom middleware.
- **Minimal**: Only `spatie/laravel-package-tools` and `illuminate/support` are required.

---

🖦 License
---------

[](#-license)

MIT License © [Digital Infinity](https://digitalinfinity.rs)

###  Health Score

34

—

LowBetter than 77% of packages

Maintenance65

Regular maintenance activity

Popularity5

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity51

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

4

Last Release

214d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/dc897661efd3f9b1d0b6cee6fa6eb2f5535819e0c2e0a0adab66b4d6002133be?d=identicon)[gorankrgovic](/maintainers/gorankrgovic)

---

Top Contributors

[![gorankrgovic](https://avatars.githubusercontent.com/u/12137730?v=4)](https://github.com/gorankrgovic "gorankrgovic (10 commits)")

---

Tags

middlewarelaravelstatelessonboardinguser flow

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/dgtlinf-user-onboarding/health.svg)

```
[![Health](https://phpackages.com/badges/dgtlinf-user-onboarding/health.svg)](https://phpackages.com/packages/dgtlinf-user-onboarding)
```

###  Alternatives

[spatie/laravel-livewire-wizard

Build wizards using Livewire

4061.0M4](/packages/spatie-laravel-livewire-wizard)[nativephp/mobile

NativePHP for Mobile

82724.0k43](/packages/nativephp-mobile)[tonysm/importmap-laravel

Use ESM with importmap to manage modern JavaScript in Laravel without transpiling or bundling.

148399.8k1](/packages/tonysm-importmap-laravel)[bezhansalleh/filament-google-analytics

Google Analytics integration for FilamentPHP

205144.8k5](/packages/bezhansalleh-filament-google-analytics)[timacdonald/has-parameters

A trait that allows you to pass arguments to Laravel middleware in a more PHP'ish way.

228271.7k2](/packages/timacdonald-has-parameters)[laracraft-tech/laravel-useful-additions

A collection of useful Laravel additions!

58109.4k](/packages/laracraft-tech-laravel-useful-additions)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
