Loading States
When a user interacts with your Livewire components, providing visual feedback during network requests is essential for a good user experience. Livewire automatically adds a data-loading attribute to any element that triggers a network request, making it easy to style loading states.
Livewire also provides the wire:loading directive for toggling elements during requests. While wire:loading is simpler for basic show/hide scenarios, it has more limitations (requiring wire:target for specificity, doesn't work well with events across components, etc.). For most use cases, you should prefer using data-loading selectors as demonstrated in this guide.
Basic usage
Livewire automatically adds a data-loading attribute to any element that triggers a network request. This allows you to style loading states directly with CSS or Tailwind without using wire:loading directives.
Here's a simple example using a button with wire:click:
<button wire:click="save" class="data-loading:opacity-50"> Save Changes</button>
When the button is clicked and the request is in-flight, it will automatically become semi-transparent thanks to the data-loading attribute being present on the element.
How it works
The data-loading attribute is automatically added to elements that trigger network requests, including:
- Actions:
wire:click="save" - Form submissions:
wire:submit="create" - Property updates:
wire:model.live="search" - Events:
wire:click="$dispatch('refresh')"
Importantly, the attribute is added even when dispatching events that are handled by other components:
<button wire:click="$dispatch('refresh-stats')"> Refresh</button>
Even though the event is picked up by a different component, the button that dispatched the event will still receive the data-loading attribute during the network request.
Styling with Tailwind
Tailwind v4 and above provides powerful selectors for working with the data-loading attribute.
Basic styling
Use Tailwind's data-loading: variant to apply styles when an element is loading:
<button wire:click="save" class="data-loading:opacity-50"> Save</button>
Showing elements during loading
To show an element only while loading is active, use the not-data-loading:hidden variant:
<button wire:click="save"> Save</button> <span class="not-data-loading:hidden"> Saving...</span>
This approach is preferred over hidden data-loading:block because it works regardless of the element's display type (flex, inline, grid, etc.).
Styling children
You can style child elements when a parent has the data-loading attribute using the in-data-loading: variant:
<button wire:click="save"> <span class="in-data-loading:hidden">Save</span> <span class="not-in-data-loading:hidden">Saving...</span></button>
The in-data-loading: variant will trigger if any ancestor element (no matter how far up the tree) has the data-loading attribute. This can lead to unexpected behavior if you have nested loading states.
Styling parents
Style parent elements when they contain a child with data-loading using the has-data-loading: variant:
<div class="has-data-loading:opacity-50"> <button wire:click="save">Save</button></div>
When the button is clicked, the entire parent div will become semi-transparent.
Styling siblings
You can style sibling elements using Tailwind's peer utility with the peer-data-loading: variant:
<div> <button wire:click="save" class="peer"> Save </button> <span class="peer-data-loading:opacity-50"> Saving... </span></div>
Complex selectors
For more advanced styling needs, you can use arbitrary variants to target specific elements:
<!-- Style all direct children when loading --><div class="[&[data-loading]>*]:opacity-50" wire:click="save"> <span>Child 1</span> <span>Child 2</span></div> <!-- Style specific descendant elements --><button class="[&[data-loading]_.icon]:animate-spin" wire:click="save"> <svg class="icon"><!-- spinner --></svg> Save</button>
Learn more about Tailwind's state variants and arbitrary selectors in the Tailwind CSS documentation.
Advantages over wire:loading
The data-loading attribute approach offers several advantages over the traditional wire:loading directive:
-
No targeting needed: Unlike
wire:loadingwhich often requireswire:targetto specify which action to respond to, thedata-loadingattribute is automatically scoped to the element that triggered the request. -
More elegant styling: Tailwind's variant system provides a cleaner, more declarative way to style loading states directly in your markup.
-
Works with events: The attribute is added even when dispatching events that are handled by other components, something that was previously difficult to achieve with
wire:loading. -
Better composition: Styling with Tailwind variants composes better with other utility classes and states.
Tailwind 4 requirement
The in-data-loading:, has-data-loading:, peer-data-loading:, and not-data-loading: variants require Tailwind CSS v4 or above. If you're using an earlier version of Tailwind, you can still target the data-loading attribute using the data-loading: syntax or standard CSS.
Using with plain CSS
If you're not using Tailwind, you can target the data-loading attribute with standard CSS:
[data-loading] { opacity: 0.5;} button[data-loading] { background-color: #ccc;}
You can also use CSS to style child elements:
[data-loading] .loading-text { display: inline;} [data-loading] .default-text { display: none;}
See also
- wire:loading — Show and hide elements during requests
- Actions — Display feedback during action processing
- Forms — Indicate form submission progress
- Lazy Loading — Show loading states for lazy components