stx adds x-cloak to any element whose own text content is a reactive . For a label that is fine. For a control, it means the thing the user is supposed to click does not exist until the bundle runs — and the failure is invisible in review, because it looks correct in a browser with fast JS.
This has now cost me three separate bugs in one codebase, which is what makes it worth filing rather than just working around.
What happens
<button @click="submit()" :disabled="busy()">{{ busy() ? 'Sending…' : 'Send invitation' }}</button>yw() in the render pipeline sees a mustache as the element's text and stamps x-cloak. x-cloak is display:none. So the only control on the panel is invisible on first paint, on a slow connection, and permanently if the bundle fails to load.
The same rule applied to a container is worse. An x-for whose rows carry reactive mustaches cloaks the container, and an entire panel ships as a blank box:
<div class="panel">
<template x-for="opt in options">
<button>{{ opt.label }}</button> <!-- cloaks the panel body -->
</template>
</div>That one shipped to production as an empty install-instructions panel, seen by exactly the users who had nothing else to look at.
Why the cloak is right and still wrong
Cloaking is the correct default for a value that genuinely cannot be known server-side — it prevents a flash of . The gap is that there is no way to say "here is the value to render until then", so the only options are "cloak it" or "do not make it reactive".
The workaround is to keep the element's text static and move reactivity to attribute bindings, which do not cloak:
<button @click="submit()" :disabled="busy()">Send invitation</button>
<span :show="busy()" style="display:none">Sending…</span>That works. It is also non-obvious, has to be rediscovered every time, and reads like a style preference rather than the correctness requirement it is.
What would help, roughly in order of preference
- Server-render the initial value when the expression is evaluable at render time.
withbusyinitialised tofalsehas a knowable first value. Cloak only when it genuinely cannot be resolved. - An explicit opt-out —
x-no-cloak, or an initial-value attribute — so an author can say "render this and let hydration correct it". - Failing both, warn. A build-time or dev-mode warning when
x-cloaklands on an element that is or contains an interactive control (button,a[href],input) would have caught all three of mine at the moment I wrote them.
Even just documenting the rule — reactive text cloaks, attribute bindings do not — would be a real improvement; I could not find it stated anywhere.
Context
Found across three panels in one app: an install-snippet panel (whole body blanked in production), a team invite button, and an invitation accept button. All three now use the static-text workaround, and the app has a probe asserting that nothing in those panels carries x-cloak except elements deliberately hidden at rest.