Flux version
v2.20.0 (livewire/flux and livewire/flux-pro)
Livewire version
v4.4.5
Tailwind version
v4.3.3
Browser and Operating System
Chromium on Linux; reproduced with both debug and production Flux Pro bundles. The denied-storage condition below is deliberately injected before initialization, so reproducing it does not depend on a particular browser privacy setting.
What is the problem?
An optional saved appearance preference can prevent unrelated Flux components from initializing when access to localStorage throws SecurityError.
There are two boundaries: the initial appearance read in Flux's alpine:init listener (and its fallback cleanup), and the reads/writes in @fluxAppearance. With storage denied, initialization throws before providers such as fluxModal register. A page can therefore load all its JavaScript successfully but still have unusable modals.
| Condition |
Current behavior |
Expected behavior |
Reading localStorage throws SecurityError |
Initialization throws; modal fails to open |
Use system appearance and finish registering components |
Removing the saved preference throws SecurityError |
System-appearance initialization can throw |
Apply system appearance without persistence |
Saving dark/light preference throws SecurityError |
Appearance change throws before applying the class |
Apply the choice in memory; skip persistence |
| Storage is available |
Works |
Keep existing persistence behavior |
Code snippets to replicate the problem
In an otherwise working Flux/Livewire application, render this standalone Blade view. There are no models, application variables or third-party scripts:
<!doctype html>
<html>
<head>
<script>
// Deterministic simulation of browser storage being unavailable.
Object.defineProperty(window, 'localStorage', {
get() {
throw new DOMException('Storage denied', 'SecurityError');
},
});
</script>
@fluxAppearance
</head>
<body>
<div x-data>
<button type="button" x-on:click="$flux.modal('storage-test').show()">
Open dialog
</button>
<button type="button" x-on:click="$flux.appearance = 'dark'">Dark</button>
<button type="button" x-on:click="$flux.appearance = 'light'">Light</button>
<flux:modal name="storage-test">
<p>Storage independent modal</p>
<flux:modal.close>
<flux:button>Close dialog</flux:button>
</flux:modal.close>
</flux:modal>
</div>
@fluxScripts
@livewireScripts
</body>
</html>
- Load the view and inspect the console. Click Open dialog.
- Remove the injection script, reload, and click again: this is the normal-storage control and the modal works.
- Restore the injected getter and remove only
@fluxAppearance: the runtime's own initial read still fails. This isolates the runtime from the inline appearance script.
- To exercise cleanup separately, replace the injected getter with:
Storage.prototype.removeItem = function () {
throw new DOMException('Storage denied', 'SecurityError');
};
Use a fresh browser context with no saved appearance. For the write case, override setItem instead, restore @fluxAppearance, and click Dark / Light.
Console evidence / before and after
The unpatched getter case produces SecurityError: Storage denied, followed by missing-provider errors such as fluxModal is not defined; Open dialog fails. This is an initialization failure rather than a visual rendering discrepancy.
Our real-browser matrix runs getter denial, removal denial, write denial and an available-storage control against both debug and production Pro bundles, including @fluxAppearance:
- Before: 6 failing cases; 2 normal-storage controls passing.
- After guarding the relevant storage operations: 8 passing cases, 40 assertions, covering modal open/close, dark/light application and absence of JavaScript errors.
The same unguarded runtime operations are present in the public free bundles, but the executed browser matrix above is specifically for Pro plus the shared appearance directive.
How do you expect it to work?
A failure to persist a theme preference should not prevent modal/input providers from registering or prevent a theme choice being applied for the current page.
Suggested scope: catch only SecurityError around these optional storage operations, use system when the initial read is unavailable, and still apply appearance changes in memory when persistence fails. Re-throw unrelated exceptions so real implementation defects remain visible. Avoid wrapping the entire initialization handler in a catch.
The current public appearance directive contains the unguarded get/remove/set operations; the public dist/flux-lite.min.js and dist/flux.min.js also contain the unguarded runtime appearance read and fallback cleanup.
Please confirm
I searched open/closed issues, pull requests and discussions for localStorage, SecurityError and denied-storage initialization. The appearance reports I found concerned configuration, flashing or cross-subdomain persistence, rather than this failure.
Flux version
v2.20.0 (
livewire/fluxandlivewire/flux-pro)Livewire version
v4.4.5
Tailwind version
v4.3.3
Browser and Operating System
Chromium on Linux; reproduced with both debug and production Flux Pro bundles. The denied-storage condition below is deliberately injected before initialization, so reproducing it does not depend on a particular browser privacy setting.
What is the problem?
An optional saved appearance preference can prevent unrelated Flux components from initializing when access to localStorage throws
SecurityError.There are two boundaries: the initial appearance read in Flux's
alpine:initlistener (and its fallback cleanup), and the reads/writes in@fluxAppearance. With storage denied, initialization throws before providers such asfluxModalregister. A page can therefore load all its JavaScript successfully but still have unusable modals.SecurityErrorSecurityErrorSecurityErrorCode snippets to replicate the problem
In an otherwise working Flux/Livewire application, render this standalone Blade view. There are no models, application variables or third-party scripts:
@fluxAppearance: the runtime's own initial read still fails. This isolates the runtime from the inline appearance script.Use a fresh browser context with no saved appearance. For the write case, override
setIteminstead, restore@fluxAppearance, and click Dark / Light.Console evidence / before and after
The unpatched getter case produces
SecurityError: Storage denied, followed by missing-provider errors such asfluxModal is not defined; Open dialog fails. This is an initialization failure rather than a visual rendering discrepancy.Our real-browser matrix runs getter denial, removal denial, write denial and an available-storage control against both debug and production Pro bundles, including
@fluxAppearance:The same unguarded runtime operations are present in the public free bundles, but the executed browser matrix above is specifically for Pro plus the shared appearance directive.
How do you expect it to work?
A failure to persist a theme preference should not prevent modal/input providers from registering or prevent a theme choice being applied for the current page.
Suggested scope: catch only
SecurityErroraround these optional storage operations, usesystemwhen the initial read is unavailable, and still apply appearance changes in memory when persistence fails. Re-throw unrelated exceptions so real implementation defects remain visible. Avoid wrapping the entire initialization handler in a catch.The current public appearance directive contains the unguarded get/remove/set operations; the public
dist/flux-lite.min.jsanddist/flux.min.jsalso contain the unguarded runtime appearance read and fallback cleanup.Please confirm
I searched open/closed issues, pull requests and discussions for localStorage, SecurityError and denied-storage initialization. The appearance reports I found concerned configuration, flashing or cross-subdomain persistence, rather than this failure.