An Overview Of How Livewire Works
This article provides an overview of how Livewire works, a full-stack framework in Laravel that makes it easy to create reactive interfaces without writing any JavaScript. The article covers the basics of Livewire, and how it works. It also includes snippets of code for a Livewire component and how it works behind the scenes. You can expect to gain a broad understanding of Livewire and how it can create dynamic UIs within the same language and framework without managing two separate concerns.
Introduction
Laravel is an excellent choice for building modern web applications. However, building dynamic client web interfaces, like those created with frontend frameworks like React or Vue, in Laravel may prove challenging. This is because Laravel is a server-side framework written in PHP, resulting in a suboptimal user experience due to page reloads.
To provide users with a superior experience and avoid page reloads, developers can use JS frameworks on the client side. However, this addition can significantly increase the system’s complexity. A comprehensive understanding of JavaScript and the chosen framework (React, Vue, Svelte, etc.) is necessary.
Fortunately, the answer to whether we can create dynamic UIs within the same language and framework without managing two separate concerns is a confident yes, thanks to Livewire.
Quick Intro to Livewire
Livewire is a full-stack framework in Laravel created by Caleb Porzio that makes it easy to create reactive interfaces without writing any Javascript, that’s right, no Javascript, all in PHP. This means developers can leverage the power of Laravel and Blade templates to build dynamic UIs, we can respond to user’s actions such as form submissions, scrolling, mouse movements, or button clicks, without reloading the page. This means that users can enjoy a smoother, more fluid experience when interacting with web applications built using Livewire like it was with other front-end frameworks.
A sample livewire snippets
// The Implementation logic
<?php
namespace App\Http\Livewire;
use Livewire\Component;
class CountButton extends Component
{
public $count = 0;
public function increment() {
$this->count++;
}
public function render()
{
return view('livewire.count-button');
}
}
----- Templates ----
<div>
You've clicked me {{$count}} times
<button wire:click="increment" class="p-4 bg-green">
Click Me!
</button>
</div>
-- Page ---
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
@livewireStyles
</head>
<body class="antialiased">
<livewire:count-button />
@livewireScripts
</body>
</html>
Let’s take a quick look at the snippets above. The first code block is the implementation logic for a Livewire component called CountButton
. It defines a public property called $count
that is initially set to 0, and two methods: increment()
and render()
. The increment()
method increments the $count
property by 1, and the render()
method returns the view associated with the component, which is livewire.count-button
.
The second code block is the Blade template associated with the CountButton
Livewire component. It displays the number of times the button has been clicked ($count
) and a button with the text "Click Me!". When the button is clicked, the increment()
method defined in the previous code block - the Implementation logic is called.
The third code block is a sample HTML page that includes the CountButton
Livewire component. It first includes the Livewire styles, then includes the component using the livewire:count-button
tag. Finally, it includes the Livewire scripts. When the livewire:count-button
tag is rendered, Livewire detects it and dynamically replaces it with the output of the render()
method defined in the first code block.
When you click the button, increment()
is called. This increases count
, and you can see the changes in the UI without reloading. Everything is written in Laravel, without needing to write JavaScript. The goal of this article is to give you a broad idea of how Livewire works behind the scenes.
Behind the Scenes
Before delving into the behind-the-scenes details, it’s important to note that a Livewire application is not a Single Page Application (SPA). To understand this, we must first understand the two main stakeholders: the client and the server.
- The Client: The client is the user’s web browser and is responsible for displaying and rendering the web interface(In HTML) which sends requests to the server when the user interacts with a Livewire component, There are two important points to remember. Firstly, the main language used by browsers to handle interactions is JavaScript. Secondly, in Laravel, the client is not responsible for creating and generating its web interface. Its only responsibility is rendering the UI visually for the user, handling interactions, and making requests.
- The Server: The server is the computer or system that provides resources to the client. This can include a web server that creates or generates web pages for a client’s web browser, or a database server that provides data to a client’s application. The server manages resources and provides services to the client, while the client requests and receives these services.
In Livewire, the server creates or generates the interface. However, as we saw in the last example, these interactions are written in PHP, while the view or templates are written in Laravel Blade, and the only language the client understands is JavaScript. So, how does Livewire solve this problem? Let’s take an overview of how Livewire works.
How it works
The first key thing to note with Livewire is that it is a Live Wire or a bridge that connects the client to the server, it allows the client speaks and the server to respond back to the client. A much clearer diagram would be
After the initial rendering of the page containing the Livewire component, Livewire binds some javascript event listeners to its components and watches for every action. Each action is sent to the server as an asynchronous API request, which includes the component’s state, payload, or snapshot. The server then performs the action, generates a new template and the current new state of the component, and sends it back to the client. Using Javascript, the client intelligently changes or re-renders only the relevant part of the page without reloading it.
To illustrate this process, let’s consider a step-by-step example with a Counter
component:
class Counter extends Component
{
public $count = 1;
public function increment() {
$this->count++;
}
public function decrement() {
$this->count--;
}
public function render()
{
return view('livewire.counter');
}
}
- The above
Counter
has 2 buttons for increasing the count and decreasing the count by 1. - The code snippet above contains the state and implementation logic for when either button is clicked. However, as we can see, both the
increment()
anddecrement()
functions and the state$count
are written in PHP, while the UI is obviously written in HTML and JavaScript. Additionally, note that the server and client must be kept in sync. In the server logic, the$count
variable is 1, as visually represented by the client. - Now let’s perform some actions
- When the user clicks on the increase button, the count is not automatically increased. This is because we do not have a JavaScript function to increase the count, nor do we have a
count
state or variable to increase. To solve this, Livewire makes a network request to the server. The network request contains a payload or snapshot that details the location of the component on the server and what needs to happen on the server. In our example, the request contains a payload that says, "Hey server, I am the Counter component and reside in this part of your codebase and I want you to call the increment function." - The server gets information from this payload to find out where the Livewire component is and then runs the
increment()
method. This method changes the$count
to 2. After this, the server creates a new HTML page that shows the new$count
. But, it doesn't show anything to the user; that's what the client does, So the server responds back with this newly generated HTML and the current state of the component.
- Once the server sends a response, the client receives it and compares the current HTML on the DOM (browser page) with the server HTML. If there is a difference, Livewire intelligently merges the conflict using Alpine.js.
- The user triggers the same process by clicking the decrease button. Each action corresponds to a request-response cycle between the client and the server.
In Conclusion
Here’s the thing: Livewire is way more complex than this broad overview suggests. When a request comes in, there’s actually a lot going on behind the scenes. We’re talking security checks, hydration, dehydration, UI generation, and even morphing. But don’t worry, this overview is a solid starting point for understanding the process. Thanks for reading!