Laravel Livewire Forms
If you’ve been building Laravel applications with Livewire, you might have noticed your components getting messy with validation rules, property declarations, and form logic all mixed together. That’s where Livewire Form Objects come in—a dedicated class for handling all your form logic separately. Think of Form Objects as a specialized container that holds your form data, validation rules, and submission logic in one clean, reusable package. Introduced in Livewire 3, this feature transforms how we build forms in Laravel.
The Traditional Way vs. Form Objects
class ContactUs extends Component
{
#[Validate('required|min:3')]
public $name = '';
#[Validate('required|email')]
public $email = '';
#[Validate('required|min:10')]
public $message = '';
public function submit(): void
{
$this->validate();
// Handle submission...
}
}
class ContactUs extends Component
{
public ContactForm $form;
public function submit(): void
{
$this->form->store();
}
}
Creating Your First Form Object
Generate the Form Class
php artisan livewire:form {Form name}
<?php
// php artisan livewire:form ContactForm
// This creates a new form class at `app/Livewire/Forms/ContactForm.php`:
namespace App\Livewire\Forms;
use Livewire\Form;
class ContactForm extends Form
{
public $name = '';
public $email = '';
public $message = '';
public function rules(): array
{
return [
'name' => 'required|min:3',
'email' => 'required|email',
'message' => 'required|min:10',
];
}
}
Use It in Your Component
Laravel makes this incredibly easy with Artisan:
php artisan livewire:form {Form name}<?php
namespace App\Livewire;
use Livewire\Component;
use App\Livewire\Forms\ContactForm;
class ContactUs extends Component
{
public ContactForm $form;
public function submit(): void
{
// Validate the form
$this->form->validate();
// Access form data
Contact::create([
'name' => $this->form->name,
'email' => $this->form->email,
'message' => $this->form->message,
]);
// Flash a success message
session()->flash('success', 'Message sent!');
// Reset the form
$this->form->reset();
}
public function render(): View
{
return view('livewire.contact-us');
}
}
Bind It in Your View
The syntax is simple—just prefix your form properties with
form.<form wire:submit="submit">
<div>
<label>Name</label>
<input type="text" wire:model="form.name">
@error('form.name') <span class="error">{{ $message }}</span> @enderror
</div>
<div>
<label>Email</label>
<input type="email" wire:model="form.email">
@error('form.email') <span class="error">{{ $message }}</span> @enderror
</div>
<div>
<label>Message</label>
<textarea wire:model="form.message"></textarea>
@error('form.message') <span class="error">{{ $message }}</span> @enderror
</div>
<button type="submit">Send Message</button>
</form>
Explore project snapshots or discuss custom solutions.
Why Use Form Objects?
Separation of Concerns
Reusability
// In different components
class UserProfile extends Component
{
public ContactForm $form;
}
class AdminContact extends Component
{
public ContactForm $form;
}
Easier Testing
Better Organization
Advanced Features
Custom Methods in Form Objects
class ContactForm extends Form
{
public $name = '';
public $email = '';
public $message = '';
public function rules():array
{
return [
'name' => 'required|min:3',
'email' => 'required|email',
'message' => 'required|min:10',
];
}
public function store(): Contact
{
// Validate
$this->validate();
// Create
$contact = Contact::create($this->only(['name', 'email', 'message']));
// Send notification
Mail::to('[email protected]')->send(new ContactNotification($contact));
// Form reset and retun
$this->reset();
return $contact;
}
}
public function submit(): void
{
$this->form->store();
session()->flash('success', 'Message sent!');
}
Real-Time Validation
Add live validation with the
.live modifier:<input type="email" wire:model.live="form.email">
Conditional Validation
Add live validation with the
.live modifier:public function rules(): array
{
return [
'name' => 'required|min:3',
'email' => 'required|email',
'company' => $this->is_business ? 'required' : 'nullable',
'phone' => $this->is_business ? 'required' : 'nullable',
];
}
Real-World Example
User Registration
// app/Livewire/Forms/RegistrationForm.php
class RegistrationForm extends Form
{
public $name = '';
public $email = '';
public $password = '';
public $password_confirmation = '';
public $terms = false;
public function rules(): array
{
return [
'name' => 'required|min:3',
'email' => 'required|email|unique:users,email',
'password' => 'required|min:8|confirmed',
'terms' => 'accepted',
];
}
public function register(): void
{
$this->validate();
$user = User::create([
'name' => $this->name,
'email' => $this->email,
'password' => Hash::make($this->password),
]);
Auth::login($user);
return redirect()->route('dashboard');
}
}
// app/Livewire/Register.php
class Register extends Component
{
public RegistrationForm $form;
public function register(): void
{
return $this->form->register();
}
public function render(): View
{
return view('livewire.register');
}
}
Key Takeaways
What are Form Objects?
Dedicated classes that encapsulate form data, validation, and logic—created with php artisan livewire:form FormName. Form Objects aren’t just a feature—they’re a pattern that makes your Laravel applications more maintainable and professional. Start using them today, and your future self will thank you.
Main Benefits:
- Cleaner, more maintainable components
- Reusable form logic across multiple components
- Easier testing and debugging
- Better separation of concerns
When to Use:
- Forms with complex validation rules
- Forms reused in multiple places
- Large forms with 10+ fields
- When you want cleaner, more testable code
Simple Syntax:
-
Create:
php artisan livewire:form ContactForm -
Use:
public ContactForm $form; -
Bind:
wire:model="form.property"
Good code is its own best documentation. Form Objects let your code speak for itself.
Thank You for Spending Your Valuable Time
I truly appreciate you taking the time to read blog. Your valuable time means a lot to me, and I hope you found the content insightful and engaging!
Frequently Asked Questions
Regular Livewire properties are defined directly in your component class, which can make large forms cluttered and hard to maintain. Form Objects are separate classes that encapsulate all form-related logic—properties, validation rules, and submission methods—in one place. This keeps your component clean and makes the form logic reusable across multiple components. You create a Form Object with php artisan livewire:form FormName and use it by declaring public FormName $form; in your component.
Yes! That's one of the biggest advantages of Form Objects. Once you create a form class, you can reuse it in as many components as you need. For example, you might have a ContactForm that's used in both a public contact page and an admin dashboard. Both components simply declare public ContactForm $form; and they'll have access to the same validation rules and logic, ensuring consistency across your application.
After calling $this->form->validate(), you can access individual properties like $this->form->name or $this->form->email. For convenience, you can also use the only() method to grab multiple fields at once: $this->form->only(['name', 'email', 'message']). This is particularly useful when creating or updating database records, as you can pass the result directly to methods like Model::create().
Absolutely! You can use Livewire's .live modifier to trigger validation as users type: <input wire:model.live="form.email">. The form will automatically validate the field in real-time using the rules you defined in your Form Object's rules() method. You can also use .blur to validate when the user leaves a field, giving you full control over the validation experience.
Yes, and this is highly recommended! Form Objects aren't just for validation—you can add any form-related logic as custom methods. For example, you might create a store() method that validates the form, creates a database record, sends notifications, and resets the form all in one place. This keeps your component methods simple (just $this->form->store()) while keeping all the complex business logic organized in the Form Object where it belongs.
Comments are closed