In Livewire, how do wire:click and wire:submit call component methods, and how are arguments passed to those methods?
answer
- public method name in the directive
- arguments are JavaScript expressions
- wire:submit prevents default itself
- form inputs disabled while in flight
- type-hinted model resolved from its id
basics
~20 swire:click="addItem(42)" and wire:submit="checkout" send a request that calls that public method on the Livewire component. Arguments are JavaScript expressions sent as JSON; type-hinted models are resolved from ids and container services are injected. wire:submit prevents the browser submit itself.
solid answer
~40 s`wire:click="addItem({{ $product->id }}, 2)"` tells Livewire to send a request that calls the **public** method `addItem()` on the component, with the arguments evaluated in the browser as JavaScript and sent as JSON. On the server, a type-hinted model parameter (`Product $product`) is resolved from the id, and container services such as a repository are injected before the passed values. `wire:submit="checkout"` on a `<form>` intercepts the submit event and calls `preventDefault()` itself, so no `.prevent` is needed, and it disables the form's inputs and submit button while the request runs. After the method returns, the component re-renders and the new HTML is morphed into the page. Because any public method can be called this way, arguments are untrusted input, just like request data.
code
html · 13 lines<div>
@foreach ($this->products as $product)
<div wire:key="p-{{ $product->id }}">
{{ $product->name }}
<button wire:click="addItem({{ $product->id }}, 1)">Add</button>
</div>
@endforeach
<form wire:submit="applyCoupon">
<input type="text" wire:model="coupon">
<button type="submit">Apply</button>
</form>
</div>go deeper
Recall the directive syntax with arguments, that the method must be public, and that wire:submit prevents the default submit.
Explain how arguments are evaluated in the browser, sent as JSON, and resolved to models or services on the server.
Treat every public method and argument as an endpoint and input, keep helpers protected, and use type hints to constrain what arrives.
Set conventions so component actions stay thin, delegate to services, and are reviewed like controller endpoints.
## Actions: public methods the browser can call An **action** in Livewire is a **public method** on the component class that the browser can invoke. Directives such as `wire:click` and `wire:submit` wire DOM events to actions: ```html <button wire:click="addItem({{ $product->id }}, 2)">Add two</button> <form wire:submit="checkout"> <input wire:model="coupon"> <button type="submit">Pay</button> </form> ``` When the event fires, Livewire sends a request carrying the component's snapshot plus a list of **calls** (method name and arguments). The server rebuilds the component, runs the method, re-renders the Blade view and returns the new HTML and state, which the browser morphs into the page. ## How arguments travel - The text inside the parentheses is a **JavaScript expression** evaluated in the browser. Blade's `{{ $product->id }}` is printed into it on the server first, so the browser sees `addItem(42, 2)`. - Strings need quotes: `wire:click="setSize('large')"`. - `$event` gives access to the DOM event, for example `wire:keydown.enter="search($event.target.value)"`. - Values are sent as JSON, so they arrive in PHP as strings, numbers, booleans, arrays or null, never as PHP objects. ## What PHP does with them The method's parameters are filled in order, with two conveniences: 1. **Model resolution**: a parameter type-hinted with an Eloquent model, `public function addItem(Product $product, int $qty)`, is looked up from the passed id, much like route model binding. 2. **Dependency injection**: parameters type-hinted with a class from the service container, `public function checkout(CartService $cart)`, are resolved by the container; the values from the browser fill the remaining parameters. ## wire:submit specifics | Behaviour | `wire:click` | `wire:submit` | |---|---|---| | Needs `.prevent` to stop default | Only for links and submit buttons | No, calls `preventDefault()` itself | | Disables form inputs while running | No | Yes, inputs and submit button inside the form | | Typical use | Buttons, toggles, row actions | Saving a whole form | `wire:submit` disables the form's controls during the request, which stops a double submit on a slow connection. ## Other listeners and modifiers `wire:` accepts any browser event name (`wire:keydown`, `wire:mouseenter`, `wire:change`), and modifiers such as `.prevent`, `.stop`, `.once`, `.debounce`, `.window`, `.outside` and key aliases like `.enter` or `.escape` come from Alpine's `x-on`, which Livewire uses underneath. ## What cannot be called - **Protected and private methods** cannot be called; the request fails with a method-not-found error. - `render()` is excluded even though it is public. - A method marked `#[Computed]` is read-only and throws if called as an action. Everything else that is public **can** be called from the browser's devtools, whether or not the template references it. That is why action parameters are treated as untrusted input and why helpers that must not be reachable are kept protected. ## Ending an action An action usually just changes state and lets the component re-render. It can also: - **Redirect**: `return redirect()->to('/cart');` or `$this->redirect('/cart')`; the browser navigates after the response. - **Return a value**: the value is sent back with the response and is available to JavaScript that called the action; a `wire:click` in Blade simply ignores it. - **Dispatch events** for other components, as in the example below. - **Throw**: a validation exception fills the error bag and the view shows errors; other exceptions surface like any Laravel error. ## A cart example On a product page, an add-to-cart button calls an action on the product card component: ```php public function addItem(Product $product, int $qty = 1): void { Cart::for(auth()->user())->add($product, $qty); $this->dispatch('cart-updated'); } ``` The action runs on the server, the card re-renders, and the dispatched event lets a separate cart counter in the header refresh itself.
- In a Livewire action, what happens if the method is protected instead of public?It cannot be called from the browser. Livewire only dispatches calls to public methods defined on the component (excluding `render()`), so a call to a protected method fails with a method-not-found error. That is the standard way to keep internal helpers out of reach of devtools.
- Why does a Livewire action receive "42" as a string when a method parameter is untyped?Arguments are sent as JSON from a JavaScript expression. `addItem({{ $id }})` prints a number, but values read from inputs or `$event.target.value` are strings. Add scalar type hints such as `int $qty` so PHP coerces them, and validate them as you would request input.
saying these in an interview costs you the question
- wire:submit needs .prevent or the browser posts the form
- Protected methods can be called from wire:click
- Only methods referenced in the template can be called
- Arguments arrive as PHP objects exactly as they were in Blade
- wire:click reloads the page to run the method