In a Livewire component, how do wire:loading and wire:target show a spinner only while one specific action is running?
answer
- hidden until a request is in flight
- wire:target names the action or property
- .delay waits 200 ms by default
- scoped to its own component
- v4: data-loading on the trigger
basics
~20 swire:loading hides an element until the component sends a request and shows it until the response arrives; adding wire:target="refreshChart" limits it to requests that call that action, and .delay avoids a flash on fast responses.
solid answer
~40 sAn element with `wire:loading` is hidden by default and shown while its component has a request in flight. Without `wire:target` it reacts to **every** request of that component, so a spinner for **Refresh chart** would also flash while an unrelated filter updates. `wire:target="refreshChart"` scopes it to requests that call that action; the target can also be a property name, several names separated by commas, an action with specific arguments, or an exclusion with `wire:target.except`. Modifiers change what toggles: `.remove` inverts it, `.class="opacity-50"` toggles classes, `.attr="disabled"` toggles an attribute, and `.delay` shows it only if the request takes longer than 200 ms (aliases from `.shortest`, 50 ms, to `.longest`, 1000 ms). Livewire 4 also puts a `data-loading` attribute on the element that triggered the request, which is often simpler to style than `wire:loading`.
code
html · 14 lines<div>
<select wire:model.live="range">
<option value="week">Week</option>
<option value="month">Month</option>
</select>
<button wire:click="refreshChart" class="data-loading:opacity-50">
Refresh chart
</button>
<span wire:loading.delay wire:target="refreshChart, range">
Crunching sales figures...
</span>
</div>go deeper
Recall that wire:loading shows during a request, wire:target scopes it to an action or property, and .delay avoids flashes.
Explain the per-component scope, targeting by arguments or with .except, and when data-loading on the trigger is simpler.
Choose feedback per interaction so slow dashboard actions feel responsive without flashing indicators on fast ones.
Set a team convention between data-loading styling and wire:loading directives so loading states stay consistent across screens.
## The problem: feedback during a round trip Every **Livewire** interaction is an HTTP request: the browser sends the component's state and the action to run, the server re-renders, and the browser morphs the new HTML in. On a sales dashboard, clicking **Refresh chart** might take a second while the server aggregates the month's orders. Without feedback, users click again. Livewire's loading-state tools show something while that request is in flight, without any hand-written JavaScript. ## wire:loading basics - `wire:loading` on an element **hides** it by default and **shows** it while the component has a request in flight. - `wire:loading.remove` does the opposite: visible by default, hidden during the request. - `wire:loading.class="opacity-50"` adds classes during the request; `.class.remove` removes them. - `wire:loading.attr="disabled"` sets an attribute during the request, useful for buttons that are not form submit buttons. - When shown, the element gets `display: inline-block` unless you add a display modifier such as `.flex`, `.block` or `.grid`. An important scoping rule: an element with `wire:loading` reacts only to requests of the **component it lives in**, not to requests from other components on the page. ## wire:target: which request counts Without a target, the indicator reacts to every request its component makes. That is rarely what you want on a dashboard where a date filter, an export button and a refresh button live in one component. | `wire:target` value | Shows while | |---|---| | `refreshChart` | a request calls `refreshChart` | | `refreshChart, exportCsv` | either action is called | | `refreshChart('month')` | that action is called with that argument | | `range` | the `range` property is being updated | | `wire:target.except="exportCsv"` | any request except one calling `exportCsv` | ## Avoiding the flash: .delay On a fast response a spinner that appears for 30 ms is noise. `wire:loading.delay` waits **200 ms** before showing the element and never shows it if the response arrives first. Aliases tune the threshold: `.shortest` 50 ms, `.shorter` 100 ms, `.short` 150 ms, `.long` 300 ms, `.longer` 500 ms, `.longest` 1000 ms. ## data-loading in Livewire 4 Livewire 4 adds a `data-loading` attribute to the element that **triggered** the request (a clicked button, a submitted form, an input with a live binding) for as long as the request is in flight. You can style it with CSS (`[data-loading] { opacity: .5 }`) or Tailwind's `data-loading:` variant. Because it is attached to the trigger itself, it needs no `wire:target`, and it still appears when the button dispatches an event handled by another component. The Livewire docs recommend it for most cases and keep `wire:loading` for simple show/hide indicators elsewhere on the page. ## Common mistakes 1. Forgetting `wire:target`, so the chart spinner flashes whenever any field updates. 2. Expecting a `wire:loading` element in one component to react to a request made by another component. 3. Using a plain spinner with no `.delay` on actions that are usually instant. 4. Using `wire:loading` to disable a form's submit button by hand, when Livewire already disables the submit button and marks inputs read-only during a `wire:submit` request. ## What an interviewer listens for A junior answer describes show-while-loading. A good answer adds `wire:target` scoping, the per-component boundary and `.delay`; a strong one mentions Livewire 4's `data-loading` and explains why scoping to the trigger element is less error-prone than naming targets. ## Choosing between the tools on a dashboard | Need | Tool | |---|---| | dim the button the user clicked | `data-loading` styling on that button | | show a message elsewhere for one action | `wire:loading` plus `wire:target` | | hide stale figures while a filter reloads | `wire:loading.remove` targeting the filter property | | block double clicks on an action button | `wire:loading.attr="disabled"` | | avoid flicker on usually-fast actions | add `.delay` or a longer alias | On the sales dashboard, the **Refresh chart** button dims itself through `data-loading`, a "Crunching sales figures..." note appears only for `refreshChart` and range changes, and the note waits 200 ms so a cached response never flashes it. None of this needs custom JavaScript, which is the point: loading feedback in Livewire is declared in the template next to the element that causes the request.
- Why might a spinner under a parent component stay hidden while a child component is loading?`wire:loading` listens only to requests of the component it is declared in. A request made by a child component is that child's request, so the parent's indicator never sees it. Put the indicator inside the child, or style the triggering element with `data-loading`.
- When would you choose wire:loading.attr="disabled" instead of relying on form handling?When the button is not a form submit button, such as a `wire:click` export button. During a `wire:submit` request Livewire already disables the submit button and makes inputs read-only, but a standalone action button stays clickable unless you toggle `disabled` yourself.
saying these in an interview costs you the question
- Says wire:loading reacts to requests from every component on the page.
- Omits wire:target and accepts a spinner flashing on unrelated updates.
- Thinks .delay makes the request itself wait 200 ms.
- Believes wire:target only accepts action names, never property names.
- Writes custom JavaScript fetch hooks to show a spinner for one action.