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

Symfony Stepped Form Laravel Package

lexal/symfony-stepped-form

Symfony Stepped Form lets you build multi-step, wizard-style forms in Symfony applications with step navigation, validation, and form state handling for smoother user workflows.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony package, not a Laravel one. Before anything else, confirm what you actually got: read the README's requirements block (PHP constraint, symfony/form version, whether it ships a Bundle class or is a plain library). That single detail determines whether the next ten minutes are trivial or a dead end.

  • If it ships a Bundle class — Laravel has no bundle registration. You cannot $app->register() it. You would be pulling in symfony/form, symfony/http-foundation, and the DI container piecemeal, or running it inside a small Symfony service container alongside Laravel. Treat this as "probably not worth it" unless you're already doing that.
  • If it's a plain library (no bundle, just form types + a step-flow object) — install with composer require and use it with symfony/form directly. This is the only realistic path in a Laravel app.

First use case to target: a 3-step wizard (e.g. account → address → confirm) with per-step validation and a back button that preserves entered data. Build that end to end before touching anything more complex.

Where to look first, in order:

  1. The README's full working example — copy it verbatim, don't adapt yet.
  2. The step-transition/event hook (packages in this space usually expose something like a pre/post step-switch event). This is where 90% of real requirements land (redirect on step 3, skip a step for certain users).
  3. How state is persisted between requests — session, a DTO you own, or an entity. This is the design decision that constrains everything else.

Note that class and method names below are the conventional shape for stepped-form libraries; verify them against the actual README before writing code.

Implementation Patterns

One form type per step, not one giant type. The idiomatic structure is a step-1 type, step-2 type, step-3 type, each bound to the same underlying data object. Each type then owns its own fields and its own validation. This beats a single 40-field type with conditional if ($currentStep === 2) guards, which becomes unreadable by step five.

Persist the data object, not the form. Keep the partially-filled DTO/entity in the session (or an unconfirmed row in the DB) between steps, and re-bind a fresh form to it on each request. If you persist the form state itself you inherit serialization problems and stale CSRF tokens.

Validate per step with validation groups. Map each step to a group:

$form = $this->createForm(AddressType::class, $wizard->getData(), [
    'validation_groups' => ['step_address'],
]);

Without this, the step-1 form runs the constraints for every field in the object and step 1 fails because the step-3 email isn't filled in yet. This is the single most common cause of "the wizard won't advance."

Navigation and submit handling belong in one controller action with two branches: an "advance" submit button and a "back" button. Back should not validate — just persist the raw submitted data and render the previous step.

Rendering in Laravel. Symfony form types render via Twig form themes. In Blade you either (a) render each field manually with form_row()-equivalent helpers ported over, or (b) accept the overhead of running Twig for the wizard's partials only. For anything beyond a trivial form, (b) is less painful than it sounds. If you're on Livewire or Inertia, strongly consider whether the multi-step UX you need can be done with plain Blade components and Laravel's validator instead — for most CRUD wizards it can, and you avoid the whole Symfony Forms dependency.

Testing. Because each step is its own form type, you can unit-test the step types in isolation and integration-test the controller with a sequence of POSTs, asserting the session/DTO carries forward. Write that integration test first — it's the cheapest way to catch state bugs.

Gotchas and Tips

  • The framework mismatch is the real gotcha. You're adding symfony/form, symfony/options-resolver, symfony/property-access, and a translation dependency to a Laravel app that already has a form/validation stack. The dependency weight is meaningful. Justify it against the alternative.
  • v1.0.0 means the API is not settled. A first release has no migration history, so a 1.1 that renames the step-transition hook is entirely plausible. Pin the exact version in composer.json and don't build abstractions directly against its types — wrap the wizard in a small application-level service so a future break is contained to one file.
  • CSRF tokens across steps. If you cache the rendered step partial (Blade @cache, response caching, view fragments), the token goes stale and step 2+ fails with a 419. Don't cache wizard steps.
  • File uploads on an early step disappear when the user moves to step 2, because the temp file is gone. Move uploads to permanent storage during the step that collects them, and store the path on your DTO.
  • Stale session state. A user who abandons a wizard and starts a new one can inherit the old DTO. Clear the wizard key on the first step and on successful completion.
  • Validation groups default to Default. If constraints are declared without a groups option they live in Default and will silently run on every step. Annotate each step's constraints with an explicit group at the same time you add the field.
  • Extension points worth knowing early: the step-transition event (for conditional branching and analytics), the step-navigation policy (to make steps non-linear), and the data-class mapping (to swap in your own state container). If none of these exist in v1.0.0, assume the package is opinionated and linear — verify before you design around a branching workflow.
  • Debugging tip: when a step won't advance, dump the validation errors array before assuming the step logic is at fault. Nine times out of ten it's a missing group or a constraint leaking from a later step.
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.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route