docs/loading-states.md
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.
[!tip] Prefer data-loading over wire:loading Livewire also provides the
wire:loadingdirective for toggling elements during requests. Whilewire:loadingis simpler for basic show/hide scenarios, it has more limitations (requiringwire:targetfor specificity, doesn't work well with events across components, etc.). For most use cases, you should prefer usingdata-loadingselectors as demonstrated in this guide.
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.
The data-loading attribute is automatically added to elements that trigger network requests, including:
wire:click="save"wire:submit="create"wire:model.live="search"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.
Tailwind v4 and above provides powerful selectors for working with the data-loading attribute.
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>
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.).
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>
[!warning] The in-data-loading variant applies to all ancestors The
in-data-loading:variant will trigger if any ancestor element (no matter how far up the tree) has thedata-loadingattribute. This can lead to unexpected behavior if you have nested loading states.
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.
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>
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.
The data-loading attribute approach offers several advantages over the traditional wire:loading directive:
No targeting needed: Unlike wire:loading which often requires wire:target to specify which action to respond to, the data-loading attribute 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.
[!info] Tailwind v4+ required for advanced variants The
in-data-loading:,has-data-loading:,peer-data-loading:, andnot-data-loading:variants require Tailwind CSS v4 or above. If you're using an earlier version of Tailwind, you can still target thedata-loadingattribute using thedata-loading:syntax or standard 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;
}