A getter declared in an x-data object literal is evaluated once while the scope is built and never re-evaluated, so it renders its initial answer forever. Plain property reads in the same template update correctly, and a method with the same body updates correctly.
Nothing is logged. The page renders, every other binding works, and the stale value sits next to fresh ones — which is what makes it expensive to find.
Reproduction
<div id='probe-scope' x-data="{
n: 1,
obj: { v: 'initial' },
get doubled() { return this.n * 2; },
get fromObj() { return 'got:' + this.obj.v; },
methodDoubled() { return this.n * 2; },
methodFromObj() { return 'got:' + this.obj.v; }
}">
<div id='r-direct'>{{ n }}</div>
<div id='r-objdirect'>{{ obj.v }}</div>
<div id='r-getter'>{{ doubled }}</div>
<div id='r-getterobj'>{{ fromObj }}</div>
<div id='r-method'>{{ methodDoubled() }}</div>
<div id='r-methodobj'>{{ methodFromObj() }}</div>
</div>
<script data-stx-scoped>
setTimeout(function () {
document.getElementById('probe-scope')
.__stx_execute("n = 5; obj = { v: 'updated' }");
}, 300);
</script>After the assignment:
| Binding | Rendered | Expected |
|---|---|---|
| 5 | 5 |
| updated | updated |
— getter | 2 | 10 |
— getter | got:initial | got:updated |
| 10 | 10 |
| got:updated | got:updated |
Both getters keep the value they had at scope construction. Both methods, with identical bodies, re-evaluate.
How it showed up
A status panel whose x-data had:
get coreLabel() {
if (!this.core.available) return 'Not installed';
return this.core.healthy ? 'Healthy' : 'Unhealthy';
}After a fetch assigned core, the card still read Not installed while , bound two lines below it, showed the version from the same response. The data was right, one binding was stale, and nothing indicated which.
Either outcome would help
- Make getters reactive — read them through the same proxy that tracks plain properties, so a getter is a derived value rather than a snapshot; or
- Reject them loudly — warn (or throw) when an
x-dataliteral declares an accessor, the way a template compiler can, and document that derived values belong in methods.
Silent staleness is the worst of the three, because the failure looks like a data problem rather than a reactivity one.
Related: x-data literals already have two sharp edges that fail silently in a similar way — a // comment, and a " inside the attribute. We guard all three with a test over every template now, but they would be better caught by the framework.
Environment
@stacksjs/stx0.2.382- macOS 26.3.1 arm64, Bun 1.4.3