Laravel 10 Livewire Wizard Form Tutorial with Example

Last Updated on by in Laravel

Throughout this Laravel tutorial, you will learn how to create a multi-step form in the Laravel application using Laravel form wizard using Livewire package.

Laravel livewire is a user-friendly package for developing full-stack web applications; it lowers the pain of building dynamic user interface components. You are going to understand how to use this package to create a dynamic multi-step form with a laravel form wizard.

Laravel 10 Multistep Form with Livewire Wizard Example

Form are useful for collecting user information; vice versa, they establish the communication with the site visitors. Sometimes, you need to build advanced forms that need to be categorized in multi-steps.

This tutorial will guide you from scratch about how to create a multi-step way not only but also form a wizard with the help of the livewire package in the laravel application.

This quick and simple tutorial offers you a facile example of a multi-step using bootstrap wizard UI design in laravel app with livewire library.

Generate Laravel Project

The first step begins with creating a new laravel application using the composer command:

composer create-project laravel/laravel --prefer-dist laravel-multistep-form-example

Connect with Database

Afterwards, you need to add database name, username equally important password in .env configuration file:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=database_name
DB_USERNAME=root
DB_PASSWORD=

If you are using MAMP local server in macOs; make sure to append UNIX_SOCKET and DB_SOCKET below database credentials in .env file.

UNIX_SOCKET=/Applications/MAMP/tmp/mysql/mysql.sock
DB_SOCKET=/Applications/MAMP/tmp/mysql/mysql.sock

Model and Migration

Use command to manifest laravel model by the same token migration files:

php artisan make:model Team -m

Define table values in database/migrations/create_teams_table.php file:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateTeamsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('teams', function (Blueprint $table) {
            $table->id();
            $table->string('name')->nullable();
            $table->float('price')->nullable();
            $table->longText('detail')->nullable();
            $table->boolean('status')->default(0);        
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('teams');
    }
}

Add the following code in the app/Models/Team.php file:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Team extends Model
{
    use HasFactory;
    protected $fillable = [
        'name', 
        'price', 
        'detail', 
        'status'
    ];    
}

Run migration with following command:

php artisan migrate

Install Livewire Package

Now that, you have to use the following command to install the livewire package in your laravel application:

composer require livewire/livewire

Create Livewire Component

Go to console run the artisan command to create the livewire components:

php artisan make:livewire wizard

The above command generated two files on the following path:

app/Http/Livewire/Wizard.php
resources/views/livewire/wizard.php

Go ahead and place the below code in the app/Http/Livewire/Wizard.php file:

<?php
  
namespace App\Http\Livewire;
use Livewire\Component;
use App\Models\Team;
  
class Wizard extends Component
{
    public $currentStep = 1;
    public $name, $price, $detail, $status = 1;
    public $successMsg = '';
  
    /**
     * Write code on Method
     */
    public function render()
    {
        return view('livewire.wizard');
    }
  
    /**
     * Write code on Method
     */
    public function firstStepSubmit()
    {
        $validatedData = $this->validate([
            'name' => 'required',
            'price' => 'required|numeric',
            'detail' => 'required',
        ]);
 
        $this->currentStep = 2;
    }
  
    /**
     * Write code on Method
     */
    public function secondStepSubmit()
    {
        $validatedData = $this->validate([
            'status' => 'required',
        ]);
  
        $this->currentStep = 3;
    }
  
    /**
     * Write code on Method
     */
    public function submitForm()
    {
        Team::create([
            'name' => $this->name,
            'price' => $this->price,
            'detail' => $this->detail,
            'status' => $this->status,
        ]);
  
        $this->successMsg = 'Team successfully created.';
  
        $this->clearForm();
  
        $this->currentStep = 1;
    }
  
    /**
     * Write code on Method
     */
    public function back($step)
    {
        $this->currentStep = $step;    
    }
  
    /**
     * Write code on Method
     */
    public function clearForm()
    {
        $this->name = '';
        $this->price = '';
        $this->detail = '';
        $this->status = 1;
    }
}

Next, open resources/views/livewire/wizard.blade.php file, add the following code:

<div>
    @if(!empty($successMsg))
    <div class="alert alert-success">
        {{ $successMsg }}
    </div>
    @endif
    <div class="stepwizard">
        <div class="stepwizard-row setup-panel">
            <div class="multi-wizard-step">
                <a href="#step-1" type="button"
                    class="btn {{ $currentStep != 1 ? 'btn-default' : 'btn-primary' }}">1</a>
                <p>Step 1</p>
            </div>
            <div class="multi-wizard-step">
                <a href="#step-2" type="button"
                    class="btn {{ $currentStep != 2 ? 'btn-default' : 'btn-primary' }}">2</a>
                <p>Step 2</p>
            </div>
            <div class="multi-wizard-step">
                <a href="#step-3" type="button"
                    class="btn {{ $currentStep != 3 ? 'btn-default' : 'btn-primary' }}"
                    disabled="disabled">3</a>
                <p>Step 3</p>
            </div>
        </div>
    </div>
    <div class="row setup-content {{ $currentStep != 1 ? 'display-none' : '' }}" id="step-1">
        <div class="col-md-12">
            <h3> Step 1</h3>
            <div class="form-group">
                <label for="title">Team Name:</label>
                <input type="text" wire:model="name" class="form-control" id="taskTitle">
                @error('name') <span class="error">{{ $message }}</span> @enderror
            </div>
            <div class="form-group">
                <label for="description">Team Price:</label>
                <input type="text" wire:model="price" class="form-control" id="teamPrice" />
                @error('price') <span class="error">{{ $message }}</span> @enderror
            </div>
            <div class="form-group">
                <label for="detail">Team Details:</label>
                <textarea type="text" wire:model="detail" class="form-control"
                    id="taskDetail">{{{ $detail ?? '' }}}</textarea>
                @error('detail') <span class="error">{{ $message }}</span> @enderror
            </div>
            <button class="btn btn-primary nextBtn btn-lg pull-right" wire:click="firstStepSubmit"
                type="button">Next</button>
        </div>
    </div>
    <div class="row setup-content {{ $currentStep != 2 ? 'display-none' : '' }}" id="step-2">
        <div class="col-md-12">
            <h3> Step 2</h3>
            <div class="form-group">
                <label for="description">Team Status</label><br />
                <label class="radio-inline"><input type="radio" wire:model="status" value="1"
                        {{{ $status == '1' ? "checked" : "" }}}> Active</label>
                <label class="radio-inline"><input type="radio" wire:model="status" value="0"
                        {{{ $status == '0' ? "checked" : "" }}}> DeActive</label>
                @error('status') <span class="error">{{ $message }}</span> @enderror
            </div>
            <button class="btn btn-primary nextBtn btn-lg pull-right" type="button"
                wire:click="secondStepSubmit">Next</button>
            <button class="btn btn-danger nextBtn btn-lg pull-right" type="button" wire:click="back(1)">Back</button>
        </div>
    </div>
    <div class="row setup-content {{ $currentStep != 3 ? 'display-none' : '' }}" id="step-3">
        <div class="col-md-12">
            <h3> Step 3</h3>
            <table class="table">
                <tr>
                    <td>Team Name:</td>
                    <td><strong>{{$name}}</strong></td>
                </tr>
                <tr>
                    <td>Team Price:</td>
                    <td><strong>{{$price}}</strong></td>
                </tr>
                <tr>
                    <td>Team status:</td>
                    <td><strong>{{$status ? 'Active' : 'DeActive'}}</strong></td>
                </tr>
                <tr>
                    <td>Team Detail:</td>
                    <td><strong>{{$detail}}</strong></td>
                </tr>
            </table>
            <button class="btn btn-success btn-lg pull-right" wire:click="submitForm" type="button">Finish!</button>
            <button class="btn btn-danger nextBtn btn-lg pull-right" type="button" wire:click="back(2)">Back</button>
        </div>
    </div>
</div>

Next, add custom styling in multi-step form. So create a public/multiform.css file and add the following code:

.display-none {
    display: none;
}

.multi-wizard-step p {
    margin-top: 12px;
}

.stepwizard-row {
    display: table-row;
}

.stepwizard {
    display: table;
    position: relative;
    width: 100%;
}

.multi-wizard-step button[disabled] {
    filter: alpha(opacity=100) !important;
    opacity: 1 !important;
}

.stepwizard-row:before {
    top: 14px;
    bottom: 0;
    content: " ";
    width: 100%;
    height: 1px;
    z-order: 0;
    position: absolute;
    background-color: #fefefe;
}

.multi-wizard-step {
    text-align: center;
    position: relative;
    display: table-cell;
}

Create Form Route

Open routes/web.php file and define the route to access the multistep form from view:

<?php

use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
*/

Route::get('wizard', function () {
    return view('welcome');
});

Render Multi-step Form in Blade View

Add below code in resources/views/welcome.blade.php file:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Laravel Multistep Livewire Form Example</title>
    @livewireStyles

    <link href="//maxcdn.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap">
    <link href="{{ asset('multiform.css') }}" rel="stylesheet" id="bootstrap">
</head>

<body class="mt-5">
    <div class="container">
        <div class="text-center">
            Laravel Form Wizard Example
        </div>
        <livewire:wizard />
    </div>
</body>

<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
@livewireScripts

</html>

Now you can run the app to check the multi-step form.

php artisan serve

Open the URL on the browser:

http://localhost:8000/wizard

Laravel Livewire Wizard Form Example

Eventually, the Laravel Livewire Wizard Form tutorial is over, i hope now you have clear understanding of how to build multi step form in Laravel application with Livewire Wizard package.