Things I DON'T like about Laravel Livewire... Its "Complexity"
The time has come for me to slam Livewire a little, since I almost always slam Inertia when I compare them. I always make these comparisons because they're supposedly sister technologies: both were released alongside Jetstream, in their first introduction by the Laravel team (I think it was with version 7). From then on, each has taken somewhat different paths.
However, it's still interesting to compare them because when you create a new Laravel project, you can choose between using:
- Laravel base,
- Laravel with Inertia + Vue,
- Laravel with Inertia + React,
- Laravel with Livewire.
There's no absolute superiority, although—in my humble opinion—Livewire is a bit more of an all-rounder than Inertia. But as I always say: everything has its pros and cons, depending on the type of project you want to do.
The "problems" of Livewire: Complexity and Abstraction
Now, let's get to what I don't like so much about Livewire.
My main criticism is the complexity generated between the Laravel components (Livewire), Blade, and Alpine.js. I feel like everything is starting to overlap.
For example:
You don't know when you're working with Livewire JavaScript and when you're working with Alpine. Here's an example:
<div class="flex" x-data="{ active: $wire.entangle('step') }" class="flex flex-col max-w-sm mx-auto">
When you want to achieve bidirectional communication between client and server, you end up getting into a tangle of x-data, @entangle, get etc. This doesn't happen in Inertia, where you simply define a prop, pass the data and forget about it:
<script setup>
import Layout from './Layout'
import { Head } from '@inertiajs/vue3'
defineProps({ user: Object })
</script>
<template>
<Layout>
<Head title="Welcome" />
<h1>Welcome</h1>
<p>Hello {{ user.name }}, welcome to your first Inertia app!</p>
</Layout>
</template>
Here, however, you want to know the value of state, because it's what indicates the "step-by-step" of a form, and when you update the state on the server, you also want to update it on the client. But since you can't read it directly from the get statement, the value is lost, and you end up writing extra logic just to keep things in sync.
More Options? Yes. More Code? Also.
The advantage of Livewire is that it's more tightly integrated with Laravel, giving you more options. But those options sometimes make the code more complicated to maintain.
In Inertia, since everything is directly Vue or React, it's much clearer what happens and where it happens. However, with Livewire, you have logic in Blade, logic in components, logic in Alpine, and logic in PHP. It's hard to keep track of everything.
Another case I found complicated is the communication between parent and child components (and vice versa) in Livewire.
For example, in the "step-by-step" component, which is part of the course and book you can find at Academia, I have a StepEvent that's launched from the child component, and the parent listens to it. The parent is the general component, and the children are:
// parent
class General extends Component
{
protected $listeners = ['stepEvent'];
}
//child
class Company extends Component
{
***
function back() {
$this->dispatch('stepEvent', 1);
}
When you're in one of these steps and the user interacts, the child event triggers the parent event. The parent listens with a @listener and executes the corresponding method.
This can be confusing the first time someone reads it, especially if they're unfamiliar with our code. How do you document something like this? It gets complicated.
Reverse Communication: Father to Son
In the course/book, I also showed another, more interesting example: the shopping cart.
You have a Cart component (parent) that contains several CartItems (children).
But this time, I want the parent to pass information to the child, specifically the post or cart item:
// parent
class Cart extends Component
{
function addItem()
{
$this->dispatch('addItemToCart', $this->post);
}
}
// child
class CartItem extends Component
{
***
#[On('addItemToCart')]
function add(Post $post, int $count = 1)
{***}
Since it's an entity managed by the parent, I need to pass it to the child for it to add or modify. I don't want to complicate things here with technical details, but that's basically it.
Too Many Options?
I'm not criticizing for the sake of criticizing; I love Livewire. But it does have a steeper learning curve compared to Inertia, and it's precisely because of this logic that it can seem somewhat abstract.
Plus, there are multiple ways to do the same thing.
For example:
When working with filters and events, you can define them as:
- Tags with @,
- Decorators like @url,
- Or directly as public properties.
This gives you more options, but also makes programming more complicated.
I agree to receive announcements of interest about this Blog.
I'm talking about an aspect that I DON'T like about Laravel Livewire, which is the additional complexity that is due to the great flexibility of the stack.
- Andrés Cruz