Lock Server-Owned State in Livewire 3 Components with #[Locked]
Use Livewire 3's #[Locked] attribute to keep server-owned component properties authoritative while still allowing trusted server-side updates.
28 Sept 2025, 03:54 UTC

The problem: browser requests can override server-owned values
In Livewire, public component properties are often synchronized with the browser. If a property holds an invoice ID, team ID, or authorization context, a malicious or accidental client-side change can arrive with the next request. Without protection, the server may treat that browser-supplied value as authoritative.
Use #[Locked] in Livewire 3.x to tell Livewire to ignore client attempts to change a specific public property. Trusted server-side code—such as mount() or an action method—can still assign it. This keeps server-owned state authoritative without making the property permanently read-only.
Prerequisites
- A Laravel application with Livewire 3.x installed.
- A Livewire component with at least one public property you want to protect.
- Ability to edit PHP component files in the project.
Add the lock to a component property
Import the attribute, annotate the property, and initialize it through trusted server code. Keep values the browser should edit in separate, unlocked properties.
<?php
namespace App\Livewire;
use Livewire\Component;
use Livewire\Attributes\Locked;
class InvoiceEditor extends Component
{
#[Locked]
public int $invoiceId;
public string $draftNotes = '';
public function mount(int $invoiceId): void
{
$this->invoiceId = $invoiceId;
}
public function saveNotes(): void
{
// Trusted server code may still assign a locked property.
// $this->invoiceId = $anotherTrustedId;
// Persist $this->draftNotes for $this->invoiceId here.
$this->dispatch('saved');
}
public function render()
{
return view('livewire.invoice-editor');
}
}
In the Blade template, avoid binding the locked property to an editable input. Pass it through the route or set it in mount() from a trusted source:
<div>
<span>Invoice: {{ $invoiceId }}</span>
<textarea wire:model="draftNotes"></textarea>
<button wire:click="saveNotes">Save notes</button>
</div>
Run these commands from the project root. No special permissions are required beyond normal file-edit access. Replace App\Livewire\InvoiceEditor with your actual component namespace.
Keep editable values separate
Do not lock ordinary interactive state such as form drafts, search terms, or UI toggles. Locking them rejects valid user input and makes failures harder to diagnose. Use one locked property for the server-owned identifier and one unlocked property for the value the user edits.
Expected checks
- Render the component in a local browser. Confirm the locked value matches the value passed through the route or
mount(). - Trigger a legitimate server action, such as clicking Save. Confirm the unlocked editable property updates and the locked value remains unchanged.
- Using browser developer tools, alter the request payload or a hidden field to supply a different locked value, then trigger an action. Confirm Livewire ignores the change and the server keeps the original authoritative value. Inspect the application logs if the request errors.
- Confirm the editable input still updates normally when the user types in it.
Exact rejection behavior—whether Livewire silently ignores the value, returns a validation-style response, or logs an error—can vary by Livewire 3.x patch version. Do not assume a specific exception type; check the response and logs in your environment.
Diagnostic decision: a legitimate update fails
- If trusted server code cannot change the value, confirm the assignment happens inside a server-side method such as
mount()or an action, not in a client-bound setter. - Confirm
#[Locked]is applied to the intended property and imported fromLivewire\Attributes\Locked. - If tampering still appears possible, add the attribute, redeploy, and repeat the client-side modification test.
Limitations
#[Locked]is not authentication or authorization. A locked invoice ID does not prove the current user may access that invoice. Revalidate record access on the server.- The attribute does not hide the value. It may still appear in rendered markup, logs, or browser state.
- Do not treat it as a complete anti-tampering control for sensitive actions; enforce server-side policy for every state-changing request.
Recovery
If locking a property blocks valid UI operations, remove #[Locked] from that property and redeploy the component. Because this is a code change, there is no database rollback; reverting the attribute restores the previous client-sync behavior.
Practical verification
Create a small test component with one locked property and one ordinary editable property. Render it locally, run a legitimate server action, then attempt a direct client-side update of the locked property. Verify the server rejects the mutation and leaves the authoritative value unchanged. Run your project's automated tests and confirm the installed Livewire major version matches the attribute namespace used here.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.