AlpineJS with Laravel LiveWire

Mar 13, 2021

Table of Contents

Problem

When building a website with LiveWire you may want a file upload preview, an image carousel, or other things that are best done client-side. There’s a number of challenges when first setting up client-side rendering with LiveWire. How do we access and update PHP data? And can we listen for changes in that data? What if we only want part of our component to be handled client-side?

Solution

Alpine is a lightweight tool for adding a touch of client-side rendering to your app. It has attributes and properties that allow you to access, update, and listen for LiveWire PHP data.

Example: Component to show latency

As an example let’s build a LiveWire component that shows the latency (i.e. ping time) between the browser and server when a user clicks a button. To do this we’ll need to store the start time on the client, send a request to the server, then subtract the start time from the current time when the server responds.

Animated graphic of ping component

What we will be building

First we’ll need to add Alpine’s script file to our project.

<head>
    ...
    <script src="https://cdn.jsdelivr.net/gh/alpinejs/[email protected]/dist/alpine.min.js" defer></script>
</head>

Let’s start out with a basic LiveWire component that has a property that stores the latency and a method that can toggle the showLatency property.

public $latency = 0;

public $showLatency = false;

public function toggle() {
    $this->showLatency = !$this->showLatency;
}

public function render()
{
    return view('livewire.ping');
}

Our template file also looks pretty standard:

<button type="button" wire:click="toggle">
    {{ $showLatency ? "Stop Ping" : "Start Ping" }}
</button>

<!-- We need to hide/show this based on $showLatency -->
<div>
    <!-- Latency will be shown here -->
    <h1></h1>
</div>

Now comes the Alpine client-side magic. The main way to use Alpine is through attributes and properties. Attributes are put inside HTML tags and are used to store state, decide whether the component should be displayed, what text should be in the component, and 12 other things.

<div x-data="{ latency: 0, showLatency: false }" x-show="showLatency">
    <h1 x-text="latency + 'ms'"></h1>
</div>

The x-data attribute allows us to store state such as the latency and showLatency variables. We can use these variables in other attributes, x-show causes the component to only be shown when showLatency is true. The x-text attribute inserts the variable as text, in this case it would show 0ms. But wait! This isn’t using the state from LiveWire, it’s creating its own. To share state between Alpine and LiveWire we can use the feature @entangle.

<div x-data="{ latency: @entangle('latency'), showLatency: @entangle('showLatency') }" x-show="showLatency">
    <h1 x-text="latency + 'ms'"></h1>
</div>

The @entangle feature means that when the value is changed on the client or server, it is updated on the other side. Now when we click the show button, the latency value will be displayed. We still need to calculate and update the latency. To do this we will use Alpine properties to generate and set the latency.

First let’s lay the groundwork by adding a method called ping to our LiveWire component. When called it returns a pong event with the start time value it was passed.

public function ping($start)
{
    $this->emit('pong', $start);
}

Now in our template we add the x-init attribute that runs when the component is rendered. We listen for the pong event and set the latency variable to be the start time subtracted from the current time. Finally, we have a setInterval that calls the ping method every second. Interaction with the server is done through the $wire property. It gives us access to everything inside the LiveWire component through its various methods.

<div ... x-init="
    $wire.on('pong', (start) => $wire.set('latency', Date.now() - start));
    setInterval(() => $wire.ping(Date.now()), 1000);
">
    <h1 x-text="latency + 'ms'"></h1>
</div>

There we have it! A client-side rendered component that shares server-side state. What we built displays the latency between the browser and server. It can even be toggled between hidden and shown.

Discussion

LiveWire has great support for Alpine. It’s easy to add to a project, and you can just use it when needed. If you’re making a modal, a dropdown, a file preview, or anything else that can be done with simple client-side rendering, Alpine is a great solution.