In a Livewire component's Alpine code, how do $wire reads, writes and method calls differ in when they reach the server?
answer
- Alpine ships inside Livewire's bundle
- reads are local and reactive
- assignment waits for the next request
- $set is live unless the third argument is false
- method calls return a promise
basics
~20 sReading $wire.guests and assigning $wire.guests = 3 stay in the browser; the assignment rides along with the next Livewire request. $wire.$set() sends a request by default, and calling $wire.checkAvailability() always does, resolving with the PHP return value.
solid answer
~40 sLivewire bundles Alpine and gives every Alpine expression inside a component a `$wire` proxy for that component. Reading `$wire.guests` is local and reactive, so `x-text="$wire.guests"` updates as the value changes. Assigning `$wire.guests = 3` also stays local: bindings react at once, and the new value is queued and sent with the next request. `$wire.$set('guests', 3)` sends a request immediately unless you pass `false` as the third argument. Calling a method, `$wire.checkAvailability()`, sends a request and returns a promise that resolves with the PHP method's return value; `$wire.$refresh()` just re-renders. Pure UI state, such as whether a dropdown is open, belongs in `x-data` and never needs the server.
code
html · 14 lines<div>
<span x-text="$wire.guests"></span> guests
<!-- local write: sent with the next request -->
<button type="button" x-on:click="$wire.guests++">+1</button>
<!-- immediate request, then use the PHP return value -->
<div x-data="{ price: null }">
<button type="button" x-on:click="price = await $wire.quote()">Quote</button>
<span x-show="price" x-text="price"></span>
</div>
<button type="button" wire:click="book">Book</button>
</div>go deeper
Recall the three moves: read $wire.prop, assign $wire.prop = value (sent later), and call $wire.method() (sent now, returns a promise).
Explain deferred versus live writes, the live flag on $set, and why Alpine-only state avoids round trips entirely.
Show where you draw the line between Alpine and Livewire state in a real form, and how you keep request counts and update hooks predictable.
Set team guidance for which interactions stay client-side and which go to PHP, balancing latency, server load and a single source of truth.
## Alpine comes with Livewire **Alpine.js** is a small JavaScript framework driven by HTML attributes (`x-data`, `x-show`, `x-on`, `x-text`). **Livewire** ships Alpine inside its own script, together with plugins such as morph, intersect, collapse, focus, persist, mask, sort, resize and anchor, and starts it for you. You do not install Alpine separately; loading a second copy from a CDN or your own bundle makes Livewire log "Detected multiple instances of Alpine running" and causes subtle bugs. If you need your own Alpine plugins, import both from Livewire's ESM build and call `Livewire.start()` yourself, with `@livewireScriptConfig` in the layout. ## The `$wire` object Inside any Livewire component's markup, Alpine expressions can use the magic **`$wire`**: a JavaScript proxy for the PHP component. Public properties appear as fields, public methods as functions, and a set of `$`-prefixed helpers covers everything else. | Expression | Network request? | What happens | |---|---|---| | `$wire.guests` | No | Reads the client copy; reactive in Alpine expressions | | `$wire.guests = 3` | No, not yet | Updates client state; queued for the next request | | `$wire.$set('guests', 3)` | Yes | Sets and sends at once; `$set('guests', 3, false)` defers | | `$wire.checkAvailability()` | Yes | Calls the PHP method; returns a promise | | `$wire.$refresh()` | Yes | Re-renders without calling a method | | `x-data="{ open: false }"` | No | Alpine-only state, never sent | ## Reads and writes Reading is cheap: `$wire.guests` reads Livewire's client-side state for the component, which Alpine tracks, so `<span x-text="$wire.guests"></span>` re-renders when a `wire:model` input or a server response changes it. Writing by assignment is **deferred**. In a booking form, a "+1 guest" button can do `x-on:click="$wire.guests++"`; every binding on the page reacts at once, but PHP learns about it only when something else sends a request, such as the form's submit. That is ideal for keystroke-level interactions that should not each cost a round trip. `$wire.$set()` is the explicit form. Its third argument, `live`, defaults to `true`, so it sends a request straight away and runs the server's update hooks and any real-time validation. ## Method calls Every public method on the component is callable as `$wire.name(args)`. The call is sent to the server with any pending updates, and it returns a **promise** that resolves with the method's return value after the response is applied: ```html <button x-on:click="price = await $wire.quote(nights)">Get price</button> ``` Remember that the browser can call any public method with any arguments, so each action authorizes its own input. `$wire.$refresh()` sends a request that runs no method and simply re-renders the component. ## Other ways to reach `$wire` `$wire` is not limited to Alpine attributes: - **Component scripts** (a root-level `<script>` in a single-file component, or `@script` in a class-based view) get `$wire` for their own component, which is where widget set-up code lives. - **Outside any component**, the global `Livewire` object returns the same proxy: `Livewire.find(id)` by component id, `Livewire.first()`, `Livewire.getByName('booking-form')` or `Livewire.all()`. That is handy in the browser console when debugging. - **Helpers on the proxy** cover the rest: `$wire.$el` (the component's root element), `$wire.$id`, `$wire.$parent` (the parent component's `$wire`), `$wire.$watch(name, callback)` and `$wire.$dispatch(event, params)`. Whichever way you reach it, the rules above hold: property writes are local until the next request, method calls go to the server. ## Choosing the right tool 1. **Purely visual state** (open/closed, active tab, a character counter): Alpine `x-data`, no server. 2. **State PHP needs eventually**: `$wire.prop = value`, sent with the next request. 3. **State PHP must react to now** (availability depends on the date): `$wire.$set()` or `wire:model.live`. 4. **Server work with a result**: `await $wire.method()`. ## Common mistakes - Expecting `$wire.guests = 3` to trigger the PHP `updated` hook immediately; it runs only when the queued update is sent. - Installing Alpine separately and ending up with two copies. - Using `$wire` outside a Livewire component, where there is no component to resolve. - Putting every toggle through the server when an `x-data` flag would do.
- What does $wire.$set('guests', 3, false) do differently from $wire.$set('guests', 3) in Livewire?The third argument is `live`, which defaults to `true`. With `true`, Livewire sets the client value and immediately sends a request carrying the update. With `false`, it only sets the client value, exactly like `$wire.guests = 3`, and the update waits for the next request.
- How do you get data back from a Livewire action called from Alpine?`$wire.method()` returns a promise that resolves with the PHP method's return value once the response is applied, so `await $wire.quote()` works in an Alpine handler. For actions consumed mainly by JavaScript, Livewire 4's `#[Json]` attribute returns the data through the promise and rejects it on validation errors.
Writing on a notepad versus making a phone call: a $wire assignment is a note you hand over at the next meeting, while $set or a method call is picking up the phone right now.
saying these in an interview costs you the question
- Assigning $wire.guests = 3 sends a request immediately.
- You must install Alpine separately to use it with Livewire.
- $wire.checkAvailability() returns the rendered HTML, not a value.
- Reading $wire.guests triggers a server round trip.
- Every Alpine toggle should go through a Livewire property.