Protected vs. #[Locked] Public Properties: Which Keeps Internal State Safe in Livewire?
0 reputation · 08 Nov 2023, 22:00 UTC
0 reputation · 08 Nov 2023, 22:00 UTC
The core goal is to shield internal counters, caches, or derived values from being sent to the browser while still allowing UI‑bound fields to remain reactive.
Protected/Private Properties are omitted from hydration, so they never appear in the initial payload and cannot be mutated via wire:model. They also cannot be invoked as actions.
#[Locked] Public Properties keep the property on the component but mark it as invisible to the frontend; it is not serialized, yet the property can be mutated server‑side and reused across requests.
Livewire offers no enforced convention to distinguish helper methods from intentionally exposed actions. Teams must choose between relying solely on PHP visibility, adding explicit markers, or adopting naming conventions, and the interaction between #[Locked] and server‑side mutation during the same request remains undocumented across versions.
33275 reputation · 09 Nov 2023, 09:47 UTC
Both protected/private properties and #[Locked] public properties keep internal state from being sent to the browser, so they offer the same level of exposure safety.
Livewire strips any property that is not public from the initial HTML payload and from hydration. Adding the #[Locked] attribute to a public property tells Livewire to keep the property in the server‑side session but still exclude it from the payload. Consequently, neither approach allows the frontend to read or write the value via wire:model or action calls.
wire:model nor invoked as client‑side actions.#[Locked] public property is also omitted from the payload, but its value is stored in the session between requests and is available on the next hydration cycle.#[Locked] property during a request updates that server‑side value; the updated value will be used in the following request’s mount or any method that runs before rendering.protected $counter = 0;, and a #[Locked] public $internal = 0;.wire:model – the binder will ignore them, confirming they are not hydratable.$internal, submit a form that triggers another request, and log $internal in the second request – you will see the retained value, proving persistence.Do you need the internal counter to survive across requests? If yes, use #[Locked]; if the counter only needs to live for the current request, a protected property is sufficient and avoids the extra session storage.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.