Crafting Dynamic Forms with Laravel 12 + Livewire: Zero JavaScript Required

Author

Kritim Yantra

Jun 06, 2025

Crafting Dynamic Forms with Laravel 12 + Livewire: Zero JavaScript Required

Dynamic forms—where users can add/remove fields on the fly—are essential for surveys, multi-step processes, or complex data entry. Traditionally, this required intricate JavaScript. But with Laravel 12 and Livewire, you can build reactive, dynamic forms entirely in PHP. No Alpine.js, no Vue, no JS fatigue. Let’s create one together.


Why Livewire?

  • PHP-centric reactivity: Handle frontend logic in Laravel classes.
  • Zero JS boilerplate: Livewire’s DOM-diffing handles UI updates.
  • Seamless Laravel integration: Leverage Laravel’s validation, sessions, and components.

Step 1: Setup & Installation

  1. Create a Laravel 12 project:
    laravel new dynamic-forms-demo
    
  2. Install Livewire:
    composer require livewire/livewire
    

Step 2: Create a Livewire Component

Generate a ContactForm component:

php artisan make:livewire ContactForm

This creates:

  • app/Livewire/ContactForm.php
  • resources/views/livewire/contact-form.blade.php

Step 3: Build the Dynamic Form Logic

Goal: Let users add/remove multiple email fields.

Edit app/Livewire/ContactForm.php

<?php

namespace App\Livewire;

use Livewire\Component;

class ContactForm extends Component
{
    public $emails = ['']; // Start with one empty email field

    // Add a new empty email field
    public function addEmail()
    {
        $this->emails[] = '';
    }

    // Remove an email field by index
    public function removeEmail($index)
    {
        unset($this->emails[$index]);
        $this->emails = array_values($this->emails); // Reindex array
    }

    // Submit the form
    public function submit()
    {
        // Validate emails (simplified example)
        $validated = $this->validate([
            'emails.*' => 'required|email',
        ]);

        // Process data (e.g., save to database)
        // session()->flash('message', 'Emails saved!');
    }

    public function render()
    {
        return view('livewire.contact-form');
    }
}

Step 4: Design the Blade View

Edit resources/views/livewire/contact-form.blade.php:

<div class="max-w-2xl mx-auto p-6 bg-white shadow-lg rounded-lg">
    <form wire:submit.prevent="submit">
        <h2 class="text-2xl font-bold mb-6">Contact Emails</h2>
        
        <!-- Dynamic Email Fields -->
        @foreach ($emails as $index => $email)
            <div class="flex gap-3 mb-4" wire:key="email-{{ $index }}">
                <input 
                    type="email"
                    wire:model="emails.{{ $index }}"
                    placeholder="user@example.com"
                    class="w-full p-3 border rounded-lg focus:ring-2 focus:ring-blue-500"
                >
                @if ($index > 0)
                    <button 
                        type="button"
                        wire:click="removeEmail({{ $index }})"
                        class="px-4 bg-red-500 text-white rounded-lg hover:bg-red-600"
                    >
                        Remove
                    </button>
                @endif
            </div>
        @endforeach

        <!-- Add Email Button -->
        <button 
            type="button"
            wire:click="addEmail"
            class="mb-6 px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600"
        >
            + Add Another Email
        </button>

        <!-- Submit Button -->
        <button 
            type="submit"
            class="w-full py-3 bg-green-600 text-white rounded-lg hover:bg-green-700"
        >
            Save Emails
        </button>
    </form>
</div>

Step 5: Add the Component to a Route

Edit routes/web.php:

use App\Livewire\ContactForm;

Route::get('/contact', ContactForm::class);

How It Works

  1. wire:model: Binds input values to Livewire’s PHP properties (synced automatically).
  2. wire:click: Calls PHP methods like addEmail()/removeEmail() when clicked.
  3. wire:submit.prevent: Prevents default form submission; uses submit() method instead.
  4. Reactivity: Livewire re-renders the HTML when $emails changes, updating the UI.

Bonus: Validation & Feedback

Enhance submit() with real-time validation:

public function submit()
{
    $this->validate([
        'emails.*' => 'required|email',
    ], [
        'emails.*.required' => 'Each email field is required.',
        'emails.*.email' => 'Enter a valid email address.',
    ]);

    // Save data...
    session()->flash('success', 'Emails saved successfully!');
}

Display errors in Blade:

@error('emails.' . $index)
    <p class="text-red-500 text-sm mt-1">{{ $message }}</p>
@enderror

Conclusion

With Laravel 12 + Livewire, you’ve built a dynamic form that:
✅ Adds/removes fields reactively,
✅ Validates data in real-time,
✅ Requires zero custom JavaScript.

This is the power of Livewire: complex frontend interactions, written entirely in Laravel. No more context-switching between PHP and JS. Focus on your app’s logic—not the glue code.

Embrace the simplicity. Happy coding! 🚀

LIVE MENTORSHIP ONLY 5 SPOTS

Laravel Mastery
Coaching Class Program

KritiMyantra

Transform from beginner to Laravel expert with our personalized Coaching Class starting June 14, 2025. Limited enrollment ensures focused attention.

Daily Sessions

1-hour personalized coaching

Real Projects

Build portfolio applications

Best Practices

Industry-standard techniques

Career Support

Interview prep & job guidance

Total Investment
$200
Duration
30 hours
1h/day

Enrollment Closes In

Days
Hours
Minutes
Seconds
Spots Available 5 of 10 remaining
Next cohort starts:
June 14, 2025

Join the Program

Complete your application to secure your spot

Application Submitted!

Thank you for your interest in our Laravel mentorship program. We'll contact you within 24 hours with next steps.

What happens next?

  • Confirmation email with program details
  • WhatsApp message from our team
  • Onboarding call to discuss your goals

Tags

Comments

No comments yet. Be the first to comment!

Please log in to post a comment:

Sign in with Google

Related Posts