Livewire Button Component with Loading Indicator

In this Article, we will see how to customize our Button to have following Animation using Livewire and extract everything into a Blade Component so as to reuse anywhere into your application.

We are going to use the Breeze Button Component and extend it according to our needs. Its definition looks like below:

<button {{ $attributes->merge(['type' => 'submit']) }}>
    {{ $slot }}
</button>

I have removed all the CSS Classes for Clarity. We can use this Button Component inside our Livewire Component like below:

<x-button wire:click="save" wire:loading.attr="disabled">
    Save
</x-button>

What we want is that whenever this button is clicked, we want to change the Text to Saving. In order to do that we will use the wire:loading property.

<x-button wire:click="save" wire:loading.attr="disabled">
    Save
    <span wire:loading>Saving..</span>
</x-button>

So now “Saving..” will get displayed as long as the submit button is clicked. And it will get hidden when the AJAX Call has finished. However, during this AJAX Call both “Save” and “Saving..” are showing. So we also need to hide the “Save” during this AJAX Call. We can do so using wire:loading.remove

<x-button wire:click="save" wire:loading.attr="disabled">
    <span wire:loading.remove>Save</span>
    <span wire:loading>Saving..</span>
</x-button>

Now, even though this is working, this will lead to unexpected issues when you have more than 1 button on the Page. So it is always a good practice to specify that we only want to change the display of these elements when the AJAX Call corresponding to save method is being called. We can do so using wire:target

<x-button wire:click="save" wire:loading.attr="disabled">
    <span wire:loading.remove wire.target="save">Save</span>
    <span wire:loading wire.target="save">Saving..</span>
</x-button>

At this stage, you should see your buttons having the same behaviour as shared at the start of the Article. However, we can further improve the readability of our code by extracting the code to our Button Component. We want this to be as simple as following:

<x-button wire:click="save" loading="Saving..">Save</x-button>

First of all we will create a loading property in our button component and assign it a default value of false.

@props(['loading' => false])

We can read the Livewire Attributes inside our Blade Component using below code:

$attributes->wire('click')->value()

You can read more about them in the Livewire Docs.

When both loading property is present and the wire:click attribute is present, we want to insert our span tags, otherwise we will just display the Slot. So our code becomes this.

@if ($loading &amp;&amp; $target = $attributes->wire('click')->value())
    <span wire:loading.remove wire:target="{{$target}}">{{$slot}}</span>
    <span wire:loading wire:target="{{$target}}">{{$loading}}</span>
@else
    {{ $slot }} 
@endif

And our Full Blade Component is below:

@props(['loading' => false])

<button {{ $attributes->merge(['type' => 'submit']) }}>
    @if ($loading &amp;&amp; $target = $attributes->wire('click')->value())
        <span wire:loading.remove wire:target="{{$target}}">{{$slot}}</span>
        <span wire:loading wire:target="{{$target}}">{{$loading}}</span>
    @else
        {{ $slot }} 
    @endif
</button>

So now we can use our Button Component using the loading attributes as well as without the loading attribute. Hope you have enjoyed the Tutorial.

Leave a Reply

Your email address will not be published. Required fields are marked *