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

Nova Wizard Laravel Package

wdelfuego/nova-wizard

Add multi-step wizards to Laravel Nova forms. nova-wizard splits long resource forms into guided, step-by-step flows, letting you group fields into panels and control navigation per step so complex admin forms stay simple and easy to complete.

View on GitHub
Deep Wiki
Context7

Getting Started

wdelfuego/nova-wizard bolts a multi-step form experience onto Nova 4 by splitting a single "save" into a sequence of field-bearing steps. Start by installing and confirming the version aligns with your Nova:

composer require wdelfuego/nova-wizard
php artisan vendor:publish --tag=nova-wizard-assets   # custom Vue/JS assets
php artisan optimize:clear

Where to look first, in order:

  1. vendor/wdelfuego/nova-wizard/src/ — the abstract Wizard and Step base classes are short; read them before writing anything.
  2. vendor/wdelfuego/nova-wizard/resources/js/ — the Vue wizard shell. This tells you what the front-end actually submits and what shape the payload has in the controller.
  3. The README examples — copy the smallest one verbatim for a smoke test before adapting.

Minimal first use case: an "onboarding" flow that creates a single Company model plus its first User, split across three steps:

namespace App\Nova\Wizards;

use Wdelfuego\NovaWizard\Wizard;
use App\Nova\Wizards\Steps\CompanyDetails;
use App\Nova\Wizards\Steps\PrimaryContact;
use App\Nova\Wizards\Steps\ReviewStep;

class OnboardingWizard extends Wizard
{
    public function title(): string
    {
        return 'New Customer Onboarding';
    }

    public function steps(): array
    {
        return [
            new CompanyDetails(),
            new PrimaryContact(),
            new ReviewStep(),
        ];
    }
}
namespace App\Nova\Wizards\Steps;

use Laravel\Nova\Fields\Text;
use Wdelfuego\NovaWizard\Step;

class CompanyDetails extends Step
{
    public function title(): string { return 'Company'; }

    public function fields(): array
    {
        return [
            Text::make('Company Name', 'name')->rules('required|max:120'),
            Text::make('VAT Number', 'vat')->rules('nullable|alpha_num'),
        ];
    }
}

Register the wizard in App\Providers\NovaServiceProvider (or wherever you register Nova tools) using whatever registration hook the package's README documents — usually a Nova::wizards([...]) call or the convention-based app/Nova/Wizards directory. Once registered, the entry point shows up in the "Create" menu / tools drawer, and you navigate to /nova/wizards/onboarding-wizard.

Implementation Patterns

One Wizard, many Steps, one save. The intended pattern is that each Step is only about presentation and validation; the Wizard (or a bound "handler" class) owns the final save. Keep all DB::transaction() logic in the wizard, not in the step. Treat steps as view-models.

Persist across steps via the request bag, not the model. Because Nova sends the whole wizard payload back to the server on each step transition, intermediate values arrive in the request. Compute derived data inside the wizard:

public function save(Request $request): Model
{
    return DB::transaction(function () use ($request) {
        $company = Company::create($request->only(['name', 'vat']));
        $company->users()->create($request->only(['email', 'first_name', 'last_name']));
        return $company;
    });
}

Reuse your existing Resource fields. Point step fields at the same Text::make() / BelongsTo::make() instances you already use on your Nova resource — or better, call a shared static method that both resource and step use. This keeps validation rules and dependsOn consistent between the admin CRUD view and the wizard.

Conditional steps based on prior answers. Expose the raw request to the wizard's step resolver so you can return a different steps() array per request. A common pattern is a shouldShow(Request $request): bool on each step, with the wizard filtering them out — but be careful: the front-end will still render the shell expecting a stable list, so gate content rather than the number of steps unless you have confirmed the package supports dynamic steps on the front end.

Compose with Nova Actions. The cleanest "day to day" integration is a Nova Action on a Resource that redirects to the wizard with pre-filled query parameters (?company_id=123). The wizard then treats its first step as read-only context.

Reuse steps across wizards. Because a Step is a plain class with fields(), define an interface (interface SharedFieldSet { public function fields(): array; }) and have both a Step and a Resource delegate to the same FieldProvider class. This is the single highest-leverage refactor if you maintain multiple wizards.

Tests: hit the wizard's store endpoint, not the UI. Laravel testing goes a long way here:

$this->actingAs($user)
    ->postJson('/nova/wizards/onboarding-wizard', [
        'name'      => 'Acme',
        'email'     => '[email protected]',
        'first_name'=> 'Ada',
    ])
    ->assertStatus(201);

Assert against the created model, not against the redirect, since the redirect target is Nova's Vue shell and not stable.

Gotchas and Tips

  • This is a low-adoption package (14 stars, 0 dependents). Pin the version in composer.json ("wdelfuego/nova-wizard": "^1.0"), and consider forking into packages/ if you plan to rely on it for revenue-critical flows. There is no upgrade path warranty.
  • Nova 4 only. Nova 5 changed several field and component internals. Do not assume compatibility; if you're on Nova 5, plan to vendor the package yourself and patch the Vue components.
  • Assets are versioned. After every composer update, run php artisan vendor:publish --tag=nova-wizard-assets --force and php artisan nova:publish. Missing republishes are the #1 cause of "the wizard renders but buttons do nothing" — the Vue component is cached in public/vendor/nova-wizard/.
  • Validation semantics differ per step. rules() on individual fields is validated client-side per-step (nice UX), but anything that spans fields (e.g. "passwords match", "end date after start date") must be enforced again in the wizard's server-side validate(). Don't trust client-only validation.
  • dependsOn across steps is fragile. Nova's conditional field dependency resolves against the same form payload; if a field lives in step 1 and the dependent field is in step 3, the dependency may not fire until the earlier value is present in the payload. Verify with Vue DevTools → Network and inspect the JSON body of the step transition.
  • File uploads are the classic trap. Nova uploads files to the pending endpoint immediately, storing them under nova-temp/. If a user abandons the wizard mid-way, temp files leak. Add a cleanup scheduled command scanning storage/app/nova-temp for orphans older than 24h.
  • Relationship fields (BelongsTo, BelongsToMany) inside wizards. They work, but the wizard's save must run sync() on many-to-many relations manually — the wizard does not fire Nova's Resource save lifecycle. There is no afterSave hook for relationships, so build it yourself inside the transaction.
  • Authorization is on you. Check Gate/Policy at the top of save() and in authorize(), because the wizard route itself is only guarded by Nova's Nova::check() middleware — not by the target model's policy.
  • Debugging. Turn on APP_DEBUG and watch storage/logs/laravel.log plus the browser console — the Vue component swallows 422 responses into a generic "something went wrong" toast. Adding a global axios interceptor in your Nova::script() is the fastest way to surface server validation errors while developing.
  • Extension points worth knowing. The Step class is designed to be extended; override title(), fields(), and validationRules() per step. The Wizard class typically exposes title(), uriKey(), steps(), and the save hook. If those are all you need, stay on the public API; if you need to intercept step navigation, that's when you fork and edit resources/js/components/Wizard.vue and rebuild assets with npm run prod inside the package.
  • Concurrency. Two open tabs on the same wizard can double-submit. Wrap the save in DB::transaction() and add a unique constraint on the natural key (e.g. company.name + company.vat), or use a Cache::lock() keyed by user + wizard uriKey.
  • Do not mix with Nova Lens or Cards. This package is a standalone flow; trying to embed it inside a Lens or a Dashboard card is fighting the grain — build a dedicated tool menu entry instead.
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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect