wire:loading

Are you a visual learner?
Master Livewire with our in-depth screencasts
Watch now

Loading indicators are an important part of crafting good user interfaces. They give users visual feedback when a request is being made to the server, so they know they are waiting for a process to complete.

Basic usage

Livewire provides a simple yet extremely powerful syntax for controlling loading indicators: wire:loading. Adding wire:loading to any element will hide it by default (using display: none in CSS) and show it when a request is sent to the server.

Below is a basic example of a CreatePost component's form with wire:loading being used to toggle a loading message:

<form wire:submit="save">
<!-- ... -->
 
<button type="submit">Save</button>
 
<div wire:loading>
Saving post...
</div>
</form>

When a user presses "Save", the "Saving post..." message will appear below the button while the "save" action is being executed. The message will disappear when the response is received from the server and processed by Livewire.

Removing elements

Alternatively, you can append .remove for the inverse effect, showing an element by default and hiding it during requests to the server:

<div wire:loading.remove>...</div>

Toggling classes

In addition to toggling the visibility of entire elements, it's often useful to change the styling of an existing element by toggling CSS classes on and off during requests to the server. This technique can be used for things like changing background colors, lowering opacity, triggering spinning animations, and more.

Below is a simple example of using the Tailwind class opacity-50 to make the "Save" button fainter while the form is being submitted:

<button wire:loading.class="opacity-50">Save</button>

Like toggling an element, you can perform the inverse class operation by appending .remove to the wire:loading directive. In the example below, the button's bg-blue-500 class will be removed when the "Save" button is pressed:

<button class="bg-blue-500" wire:loading.class.remove="bg-blue-500">
Save
</button>

Toggling attributes

By default, when a form is submitted, Livewire will automatically disable the submit button and add the readonly attribute to each input element while the form is being processed.

However, in addition to this default behavior, Livewire offers the .attr modifier to allow you to toggle other attributes on an element or toggle attributes on elements that are outside of forms:

<button
type="button"
wire:click="remove"
wire:loading.attr="disabled"
>
Remove
</button>

Because the button above isn't a submit button, it won't be disabled by Livewire's default form handling behavior when pressed. Instead, we manually added wire:loading.attr="disabled" to achieve this behavior.

Targeting specific actions

By default, wire:loading will be triggered whenever a component makes a request to the server.

However, in components with multiple elements that can trigger server requests, you should scope your loading indicators down to individual actions.

For example, consider the following "Save post" form. In addition to a "Save" button that submits the form, there might also be a "Remove" button that executes a "remove" action on the component.

By adding wire:target to the following wire:loading element, you can instruct Livewire to only show the loading message when the "Remove" button is clicked:

<form wire:submit="save">
<!-- ... -->
 
<button type="submit">Save</button>
 
<button type="button" wire:click="remove">Remove</button>
 
<div wire:loading wire:target="remove">
Removing post...
</div>
</form>

When the above "Remove" button is pressed, the "Removing post..." message will be displayed to the user. However, the message will not be displayed when the "Save" button is pressed.

Targeting multiple actions

You may find yourself in a situation where you would like wire:loading to react to some, but not all, actions on a page. In these cases you can pass multiple actions into wire:target separated by a comma. For example:

<form wire:submit="save">
<input type="text" wire:model.blur="title">
 
<!-- ... -->
 
<button type="submit">Save</button>
 
<button type="button" wire:click="remove">Remove</button>
 
<div wire:loading wire:target="save, remove">
Updating post...
</div>
</form>

The loading indicator ("Updating post...") will now only be shown when the "Remove" or "Save" button are pressed, and not when the $title field is being sent to the server.

Targeting action parameters

In situations where the same action is triggered with different parameters from multiple places on a page, you can further scope wire:target to a specific action by passing in additional parameters. For example, consider the following scenario where a "Remove" button exists for each post on the page:

<div>
@foreach ($posts as $post)
<div wire:key="{{ $post->id }}">
<h2>{{ $post->title }}</h2>
 
<button wire:click="remove({{ $post->id }})">Remove</button>
 
<div wire:loading wire:target="remove({{ $post->id }})">
Removing post...
</div>
</div>
@endforeach
</div>

Without passing {{ $post->id }} to wire:target="remove", the "Removing post..." message would show when any of the buttons on the page are clicked.

However, because we are passing in unique parameters to each instance of wire:target, Livewire will only show the loading message when the matching parameters are passed to the "remove" action.

Multiple action parameters aren't supported

At this time, Livewire only supports targeting a loading indicator by a single method/parameter pair. For example wire:target="remove(1)" is supported, however wire:target="remove(1), add(1)" is not.

Targeting property updates

Livewire also allows you to target specific component property updates by passing the property's name to the wire:target directive.

Consider the following example where a form input named username uses wire:model.live for real-time validation as a user types:

<form wire:submit="save">
<input type="text" wire:model.live="username">
@error('username') <span>{{ $message }}</span> @enderror
 
<div wire:loading wire:target="username">
Checking availability of username...
</div>
 
<!-- ... -->
</form>

The "Checking availability..." message will show when the server is updated with the new username as the user types into the input field.

Excluding specific loading targets

Sometimes you may wish to display a loading indicator for every Livewire request except a specific property or action. In these cases you can use the wire:target.except modifier like so:

<div wire:loading wire:target.except="download">...</div>

The above loading indicator will now be shown for every Livewire update request on the component except the "download" action.

Customizing CSS display property

When wire:loading is added to an element, Livewire updates the CSS display property of the element to show and hide the element. By default, Livewire uses none to hide and inline-block to show.

If you are toggling an element that uses a display value other than inline-block, like flex in the following example, you can append .flex to wire:loading:

<div class="flex" wire:loading.flex>...</div>

Below is the complete list of available display values:

<div wire:loading.inline-flex>...</div>
<div wire:loading.inline>...</div>
<div wire:loading.block>...</div>
<div wire:loading.table>...</div>
<div wire:loading.flex>...</div>
<div wire:loading.grid>...</div>

Delaying a loading indicator

On fast connections, updates often happen so quickly that loading indicators only flash briefly on the screen before being removed. In these cases, the indicator is more of a distraction than a helpful affordance.

For this reason, Livewire provides a .delay modifier to delay the showing of an indicator. For example, if you add wire:loading.delay to an element like so:

<div wire:loading.delay>...</div>

The above element will only appear if the request takes over 200 milliseconds. The user will never see the indicator if the request completes before then.

To customize the amount of time to delay the loading indicator, you can use one of Livewire's helpful interval aliases:

<div wire:loading.delay.shortest>...</div> <!-- 50ms -->
<div wire:loading.delay.shorter>...</div> <!-- 100ms -->
<div wire:loading.delay.short>...</div> <!-- 150ms -->
<div wire:loading.delay>...</div> <!-- 200ms -->
<div wire:loading.delay.long>...</div> <!-- 300ms -->
<div wire:loading.delay.longer>...</div> <!-- 500ms -->
<div wire:loading.delay.longest>...</div> <!-- 1000ms -->