A user submits the form, email fails validation, red text appears. They click into the field and start retyping — and the error sits there, under a field they are visibly repairing. On a deferred wire:model it stays until they submit again. Livewire 4.2 added $errors.clear() to the JavaScript errors object so the browser can drop that message itself, with no request to the server.
Confirm you are on Livewire 4.2 or later#
$errors.clear() shipped in Livewire v4.2.0 (PR #9975), alongside Laravel 13 support and seven security hardening changes. Earlier 4.x releases expose the $errors object but not clear(), so a call there fails silently in a way that looks like your selector is wrong.
composer show livewire/livewire | grep versions
composer require livewire/livewire:^4.2
If you are still coming across from v3, the Livewire 3 to 4 migration guide covers the rest of the jump first — the $errors magic property is a v4 feature and does not exist in v3 at all.
Clear one field's error from the browser#
Bind the clear to the specific field's input event and pass that field's key. Inside Alpine you reach the errors object through $wire.$errors, which is the same object the wire: directives see.
<form wire:submit="save">
<input
type="email"
wire:model="email"
x-on:input="$wire.$errors.clear('email')"
>
<div wire:show="$errors.has('email')" class="text-red-600">
<span wire:text="$errors.first('email')"></span>
</div>
<button type="submit">Create account</button>
</form>
The full client-side API is has(), missing(), first(), get(), all() and clear(), documented in the Livewire validation docs. clear() with no argument empties the whole bag; with a field name it clears that key only. The wire:show and wire:text pairing above is the no-Alpine way to render the message — more on those in Livewire's client-side directives.
The version to avoid:
<!-- Don't: one keystroke in any field wipes every message on the page -->
<input type="email" wire:model="email" x-on:input="$wire.$errors.clear()">
That hides errors the user has not looked at yet, let alone fixed. Leaving them alone would have been better.
Clear the right key when the state lives in a form object#
A Livewire form object namespaces every error key with the property the form is bound to. A PostForm assigned to public PostForm $form produces form.title, not title — and $errors.clear('title') against it does nothing at all, with no error in the console.
<input
type="text"
wire:model="form.title"
x-on:input="$wire.$errors.clear('form.title')"
>
<div wire:show="$errors.has('form.title')" class="text-red-600">
<span wire:text="$errors.first('form.title')"></span>
</div>
The same prefixing applies to $this->addError() inside the form object. If you are unsure what key Livewire actually used, $wire.$errors.all() in the browser console prints the bag exactly as the client holds it.
Use resetValidation() when the server state genuinely changed#
$errors.clear() is a client-side gesture. The server's error bag is untouched, so the next time the component re-renders from the server with that error still present, the message comes back. resetValidation() is the server-side equivalent, and the two are not interchangeable.
$errors.clear() |
$this->resetValidation() |
|
|---|---|---|
| Runs | In the browser | On the server |
| Affects | The client error bag only | Laravel's error bag on the component |
| Costs a request | No | Yes, unless called inside an action you were already running |
| Survives a re-render | No — the server bag repopulates it | Yes |
The rule I use: clear in the browser for feedback, reset on the server when the underlying state actually changed — a cancelled modal, a switched tab in a wizard, a record swapped out underneath the form.
public function cancelEdit(): void
{
$this->reset('email');
$this->resetValidation('email'); // The field is genuinely blank again.
}
Per-step wizards are the clearest case for the server-side version — see per-step validation in a Livewire wizard for how that plays out across steps.
Pair the clear with wire:dirty#
A cleared error leaves a field saying nothing, which is its own kind of lie — the value is not valid, it is merely quiet. Adding a dirty indicator makes the field explain itself: the message is gone because you are editing, and the change is not saved yet.
<input
type="email"
wire:model="email"
wire:dirty.class="border-amber-500"
x-on:input="$wire.$errors.clear('email')"
>
<div wire:dirty wire:target="email" class="text-sm text-slate-500">
Unsaved — we'll check this when you save.
</div>
The directive's modifiers and wire:target scoping are covered in the unsaved-changes indicator with wire:dirty.
Check your stack against the X-Livewire header change#
Shipping in the same release: update requests now require the X-Livewire header and a JSON content type (PR #9965). The symptom is specific — initial page renders fine, every subsequent component update returns a 4xx. Three things cause it: a hand-rolled fetch() against the update endpoint, a reverse proxy stripping non-standard headers, and a WAF rule doing the same.
# Nginx: don't let an underscore/custom-header rule eat Livewire's requests.
underscores_in_headers on;
proxy_pass_request_headers on;
Custom update routes also now enforce web middleware automatically (PR #9971). If you need to inspect what the client is actually sending, Livewire's JavaScript interceptors give you the request object before it leaves the browser.
Avoid the four ways this goes wrong#
Clearing on focus instead of input destroys the information before the user has changed anything — they click the field to read the message and it vanishes. Bind to input, or to blur if you only want the message gone once they have moved on.
A cleared error does not stop the server rejecting the submit. $errors.clear('email') does not make email valid; it makes it silent. Your save() method still throws, and the message reappears on the response.
Clearing the whole bag is almost never what you want. The narrow call costs the same and does not hide problems the user has yet to see.
And $errors is not available before Livewire boots. Calling it from a bare <script> tag at the top of the page, rather than from an Alpine expression or a livewire:initialized listener, gets you an undefined property.
Prove the behaviour with a Pest test#
The client-side clear is invisible to Livewire::test() — which is precisely the point worth encoding. A test that asserts the server bag is unchanged documents the boundary so nobody later assumes the browser call reset something on the server.
<?php
use App\Livewire\Register;
use Livewire\Livewire;
it('keeps the server error bag until validation is reset on the server', function () {
Livewire::test(Register::class)
->set('email', 'not-an-email')
->call('save')
->assertHasErrors(['email' => ['email']])
// The browser's $errors.clear() never reaches here — only this does.
->call('cancelEdit')
->assertHasNoErrors('email');
});
More patterns for component tests are in testing Livewire components with Pest.
Wrapping Up#
Add x-on:input="$wire.$errors.clear('field')" to the inputs where a stale message is actually annoying — usually the two or three fields that fail most often — and leave the rest alone. Then check your proxy and WAF config against the X-Livewire header requirement before you deploy the upgrade, because that one bites in production rather than locally.
If you want validation that is genuinely live rather than optimistically quiet, Laravel Precognition validates against the real rules as the user types, and pairs well with a debounced live input when you want the request rate under control.
FAQ#
How do I clear a Livewire validation error without a server request?
Call $errors.clear('field') from the browser. In Alpine that is $wire.$errors.clear('email'), usually bound to the field's input event. It removes the message from the client-side error bag immediately with no network request. The server's error bag is untouched, so the message returns if the component re-renders from the server before the field is actually fixed.
What is $errors.clear() in Livewire 4.2?
$errors.clear() is a method added to Livewire's JavaScript errors object in v4.2.0. It clears validation error state in the browser — all keys when called with no argument, or a single key when passed a field name. It sits alongside the existing has(), missing(), first(), get() and all() methods on the same $errors magic property.
What is the difference between $errors.clear() and resetValidation()?
$errors.clear() runs in the browser and only affects the client-side copy of the error bag. resetValidation() runs on the server and clears Laravel's error bag on the component itself. A client-side clear is cosmetic and does not survive the next server render; a server-side reset does. Use the first for immediate feedback while a user types, and the second when the underlying state genuinely changed.
Why does my Livewire validation error stay on screen while typing?
Because the message only updates when the server responds. With a deferred wire:model no request is sent until submit, so the error from the last submit persists through all the typing in between. wire:model.live fixes it by revalidating on every update, at the cost of a request per keystroke — $errors.clear() gives you the same visual result without the traffic.
What is the X-Livewire header in Livewire 4.2?
It is a request header Livewire now requires on component update requests, together with a JSON content type, as part of the v4.2.0 security hardening. Livewire's own JavaScript sends it automatically. The header matters when something sits between the browser and your app — a reverse proxy, a WAF, or custom code calling the update endpoint directly — because stripping it causes updates to fail with a 4xx while the initial page render still works.
Can I clear a single field's error in Livewire?
Yes. Pass the field key: $errors.clear('email') on the client, or $this->resetValidation('email') on the server. The key has to match what Livewire used, which means the prefixed version when the property lives in a form object — form.email rather than email. Calling $errors.all() in the browser console shows the keys exactly as the client holds them.