codegenie-be/laravel-livewire-multistep-form

A lightweight, configurable multi-step form wizard for Laravel Livewire.

Maintainers

Package info

github.com/Codegenie-BE/laravel-livewire-multistep-form

pkg:composer/codegenie-be/laravel-livewire-multistep-form

Transparency log

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-08-20 14:34 UTC

This package is auto-updated.

Last update: 2026-08-20 17:06:12 UTC


README

Tests PHP Laravel Livewire License

A small, configuration-driven multi-step form component for Laravel Livewire. It provides step navigation, per-step validation, a review step, accessible markup, localized interface copy, and a validated submission hook without taking ownership of your application's persistence layer.

Requirements

The package declares support for:

  • PHP 8.2 through supported PHP 8.x releases;
  • Laravel 12 or 13;
  • Livewire 3.6+ or 4.x.

GitHub Actions verifies both recent and minimum resolvable dependency sets. The matrix covers Laravel 12/13, Livewire 3/4, PHP 8.2/8.3/8.4/8.5 where applicable, plus dedicated --prefer-lowest --prefer-stable jobs for every supported Laravel/Livewire major combination.

Installation

Install the stable package through Composer:

composer require codegenie-be/laravel-livewire-multistep-form

If Packagist or a Composer mirror has not indexed the newest release yet, the repository can still be installed directly as a VCS package:

composer config repositories.codegenie-livewire-multistep-form vcs https://github.com/Codegenie-BE/laravel-livewire-multistep-form
composer require codegenie-be/laravel-livewire-multistep-form:dev-main

Laravel package discovery registers the service provider automatically.

Basic usage

Define the fields in your application and pass them to the registered Livewire component:

<livewire:codegenie-multistep-form
    :fields="[
        'name' => [
            'default' => '',
            'rules' => 'required|string|min:2|max:120',
            'label' => 'Name',
            'step' => 1,
            'type' => 'text',
            'placeholder' => 'Your name',
        ],
        'email' => [
            'default' => '',
            'rules' => 'required|email|max:255',
            'label' => 'Email address',
            'step' => 2,
            'type' => 'email',
        ],
        'topic' => [
            'default' => '',
            'rules' => 'required|string',
            'label' => 'Topic',
            'step' => 2,
            'type' => 'select',
            'placeholder' => 'Choose a topic',
            'options' => [
                'general' => 'General question',
                'support' => 'Support',
            ],
        ],
        'message' => [
            'default' => '',
            'rules' => 'required|string|min:10|max:5000',
            'label' => 'Message',
            'step' => 3,
            'type' => 'textarea',
            'placeholder' => 'How can we help?',
        ],
    ]"
/>

Steps must start at 1 and remain consecutive. Multiple fields may share the same step.

Field configuration

Each field supports these keys:

Key Required Description
type yes One of text, email, number, tel, url, date, textarea, or select
rules conditionally Laravel validation rules as a non-empty string or an array of non-empty strings; may be empty when serverValidationRules() supplies the field rules
step yes Positive integer step number
label no Human-readable label; generated from the field name when omitted
default no Scalar or null initial value; defaults to an empty string. Select defaults are more restrictive as described below.
placeholder no Non-empty placeholder text for text-like fields, textarea, or select
options for select Non-empty value-to-label map; option values form the server-side allow-list

Every field must have at least one configured validation rule or one server-only validation rule. Unsupported field types are rejected instead of being rendered with undefined behavior. File uploads, checkboxes, radio groups, repeaters, and nested field names are intentionally outside the current scope.

Select fields

Select option keys are normalized to strings because browser and Livewire form values are string-based. The package adds the configured option keys to the same Laravel validator used for the rest of the field rules, so consumers do not need to duplicate the option list in an in: rule.

Select defaults may only be strings, integers, or null; integer defaults are normalized to strings. Booleans and floating-point defaults are rejected to avoid ambiguous browser value coercion. A non-empty select default must exist in options. The empty value is reserved for the unselected / placeholder state. On the review step, the human-readable option label is displayed instead of the raw option key.

Validation

nextStep() validates only the fields on the current step. Native form submission on an input step, including pressing Enter in a compatible control, is routed through nextStep() rather than final submission.

submit() is accepted only on the dynamic review step and validates the complete form again. A direct attempt to invoke final submission before the review step is ignored server-side. If final revalidation fails, the wizard returns to the step containing the first invalid configured field and requests focus for that field. Select values are constrained to their configured option keys as part of the same validation pass.

For Laravel rules containing pipe characters as part of the rule itself, such as a regular expression, use array rule syntax:

'rules' => [
    'required',
    'regex:/^(foo|bar)$/',
],

Server-only Laravel rules

The declarative rules value is part of the locked public Livewire field configuration. It is appropriate for ordinary serializable rules such as required|string|max:255, but rule configuration that contains application internals or Laravel rule objects should remain on the server.

Extend the component and override serverValidationRules() for those rules. Keys are the configured field names without the formData. prefix:

<?php

namespace App\Livewire;

use Codegenie\LivewireMultistepForm\Livewire\MultiStepForm;
use Illuminate\Validation\Rule;

class ContactWizard extends MultiStepForm
{
    protected function serverValidationRules(): array
    {
        return [
            'email' => [
                Rule::unique('users', 'email'),
            ],
        ];
    }
}

The returned rules are rebuilt on each Livewire request and merged with the declarative field rules. Laravel rule objects and closures can therefore be used without placing them in public Livewire state. A server-only rule may only target a configured field; unknown field names are rejected instead of silently validating unrelated data.

Configured defaults are initialized before serverValidationRules() is first evaluated during mount, so server-only rules may safely inspect default-driven formData values. A field may also leave rules empty and define its complete validation contract in serverValidationRules(). If native browser required semantics are desired, keep a simple required rule in the declarative field configuration as well; the server remains authoritative either way.

Validation errors use the same formData.* keys as the Livewire bindings and are rendered with accessible error relationships.

Handling submissions

The base package deliberately does not write to a database, send mail, or redirect to an application-specific route. A reusable UI package should not decide how your application stores submitted data.

On a valid review-step submission it:

  1. validates the complete form, including configured select allow-lists and server-only rules;
  2. calls the protected handleSubmission(array $data) extension point;
  3. dispatches the multistep-form-submitted Livewire event with the validated configured data;
  4. resets the wizard to its configured defaults.

For server-side persistence, extend the component in your application:

<?php

namespace App\Livewire;

use App\Models\ContactRequest;
use Codegenie\LivewireMultistepForm\Livewire\MultiStepForm;

class ContactWizard extends MultiStepForm
{
    protected function handleSubmission(array $data): void
    {
        ContactRequest::query()->create($data);
    }
}

Register and render that application component using normal Livewire conventions. Keep authorization, persistence, mail delivery, rate limiting, and other application-specific concerns in your application.

Localization

The default interface ships with English, Dutch, and French translations. It follows the consuming Laravel application's active locale.

Consumers may publish the translations and override individual strings:

php artisan vendor:publish --tag=livewire-multistep-form-translations

Published translations are placed under Laravel's normal vendor translation path.

Customizing the view

The default Blade view can be published when an application needs markup or styling changes:

php artisan vendor:publish --tag=livewire-multistep-form-views

The published view is placed under:

resources/views/vendor/livewire-multistep-form/

When customizing it, preserve the validation bindings, review-step submission guard, escaped review output, instance-scoped DOM IDs, and accessibility relationships.

Tailwind CSS

The package ships Blade markup with Tailwind utility classes but does not install or compile frontend assets for the consuming application.

Tailwind CSS 4

Add the package views as a source in your application's CSS entrypoint. Adjust the relative path when your CSS file lives elsewhere:

@source '../../vendor/codegenie-be/laravel-livewire-multistep-form/resources/views/**/*.blade.php';

Tailwind CSS 3

Add the package views to the content array in tailwind.config.js:

content: [
    './resources/**/*.blade.php',
    './vendor/codegenie-be/laravel-livewire-multistep-form/resources/views/**/*.blade.php',
],

You may pass six-digit hexadecimal colors for the progress indicator and primary action:

<livewire:codegenie-multistep-form
    :fields="$fields"
    primary-color="#216ef2"
    button-color="#216ef2"
/>

The package validates the color format. The consuming application remains responsible for choosing colors with sufficient contrast.

Security characteristics

  • Field configuration, current step, and color configuration are locked Livewire state.
  • Declarative field rules are public Livewire state and should not contain sensitive application internals.
  • serverValidationRules() keeps complex or sensitive rule configuration server-side.
  • User-controlled form values remain mutable and are always validated server-side.
  • Final submission is accepted only on the review step and revalidates the complete form.
  • Select values are checked against their configured server-side option allow-lists.
  • Only configured and validated fields are passed to the submission hook.
  • Review values are escaped before rendering.
  • Inline color values accept only six-digit hexadecimal values.
  • The package does not store data, manage credentials, run queues, require Redis, or call external services.

The application's own authorization, persistence, rate limiting, privacy policy, and data-retention rules remain the responsibility of the consuming project.

See SECURITY.md for vulnerability reporting guidance.

Accessibility

The default view includes:

  • semantic form, fieldset, label, and definition-list markup;
  • a progressbar with ARIA value metadata;
  • instance-scoped DOM IDs so multiple wizards can coexist on one page;
  • aria-invalid and aria-describedby on invalid controls;
  • alert semantics for validation errors;
  • explicit button types;
  • visible keyboard focus styles;
  • instance-scoped focus management after navigation, resets, submissions, and validation failures;
  • reduced-motion-aware transitions and loading indicators;
  • localized screen-reader and interface copy.

Consumers who publish their own view should preserve equivalent semantics.

Development

Install dependencies:

composer update

Run the quality gates:

composer test
composer format
composer analyse
composer audit

The repository CI additionally runs recent and minimum compatibility matrices. Larastan currently runs at level 8, and the aggregate Required checks job only passes when compatibility and quality jobs all succeed.

Contributing

See CONTRIBUTING.md. Keep changes focused and Laravel-native. New field types must include validation, rendering behavior, accessibility handling, and regression tests for their supported states.

Changelog

See CHANGELOG.md for stable release notes and unreleased changes.

License

MIT. See LICENSE.

Built and maintained by Codegenie.