Livewire Best Practices: Building Reactive UIs Without JavaScript
The Livewire Philosophy
Livewire lets you build reactive user interfaces using PHP instead of JavaScript. It's not about avoiding JavaScript entirely—it's about not needing JavaScript for the majority of interactive features. Forms, modals, filtering, pagination—all handled with familiar PHP code.
Component Design Principles
Good Livewire components are focused and encapsulated. They manage a single concern and communicate with the outside world through events.
class SearchProducts extends Component
{
public string $search = '';
public string $category = '';
public string $sortBy = 'created_at';
protected $queryString = [
'search' => ['except' => ''],
'category' => ['except' => ''],
];
public function updatedSearch(): void
{
$this->resetPage();
}
public function render(): View
{
return view('livewire.search-products', [
'products' => Product::query()
->when($this->search, fn($q) => $q->where('name', 'like', "%{$this->search}%"))
->when($this->category, fn($q) => $q->where('category', $this->category))
->orderBy($this->sortBy)
->paginate(20),
]);
}
}
Performance Optimization
Livewire sends the entire component state with each request. Minimize what you store in public properties:
// Bad: Storing full collection
public Collection $products;
// Good: Store only what you need for interactivity
public int $selectedProductId;
// Even better: Compute in render
public function render()
{
return view('livewire.products', [
'products' => $this->getProducts(),
]);
}
Lazy Loading
For components that aren't immediately visible, use lazy loading:
<livewire:heavy-component lazy />
Form Handling
Livewire 3 introduced Form Objects for cleaner form handling:
class ContactForm extends Form
{
#[Validate('required|min:3')]
public string $name = '';
#[Validate('required|email')]
public string $email = '';
#[Validate('required|min:10')]
public string $message = '';
}
class ContactPage extends Component
{
public ContactForm $form;
public function submit(): void
{
$this->form->validate();
Contact::create($this->form->all());
$this->form->reset();
$this->dispatch('contact-sent');
}
}
Communication Patterns
Components communicate through events:
// Dispatching
$this->dispatch('productSelected', productId: $id);
// Listening (in component)
#[On('productSelected')]
public function handleProductSelected(int $productId): void
{
$this->selectedProduct = Product::find($productId);
}
Alpine.js Integration
For client-side interactions that don't need server communication, use Alpine.js:
<div x-data="{ open: false }">
<button @click="open = !open">Toggle</button>
<div x-show="open" x-transition>
Dropdown content
</div>
</div>
Testing Livewire Components
Livewire provides excellent testing utilities:
public function test_can_search_products(): void
{
Product::factory()->create(['name' => 'Laravel Book']);
Product::factory()->create(['name' => 'PHP Guide']);
Livewire::test(SearchProducts::class)
->set('search', 'Laravel')
->assertSee('Laravel Book')
->assertDontSee('PHP Guide');
}
Conclusion
Livewire is a productivity multiplier for Laravel developers. Embrace its philosophy, optimize for performance, and combine it with Alpine.js for the best of both worlds.
Related Articles
Need Help With Your Project?
I respond to all inquiries within 24 hours. Let's discuss how I can help build your production-ready system.
Get In Touch