Three Attributes Architecture - Maximum Performance Without Runtime Checks
Every reactive framework faces the same dilemma: how to handle different types of properties efficiently. Most frameworks choose developer convenience over performance. We chose performance.
The Problem: Runtime Type Checking Kills Performance
Traditional frameworks mix all props together:
// React/Solid approach - looks simple, but...
<div id="static" onClick={handler} items={list}>
Behind the scenes, the framework must check EVERY prop:
// What frameworks do internally
for (const prop of props) {
if (isEvent(prop)) handleEvent(prop);
else if (isReactive(prop)) setupReactivity(prop);
else if (isList(prop)) setupListTracking(prop);
else setStaticProp(prop);
}
The Cost Adds Up
Consider a typical app with 10,000 elements and ~5 props each — roughly 50,000 prop reads. In a runtime-dispatch model, each read asks the same question ("is this reactive? an event handler? a plain attribute?") and does it on every render cycle. Any single check is trivially cheap; the point is that it's work you keep paying. Exodra pays it once, at compile time, and never again at runtime.
Death by a thousand cuts.
Our Solution: Three Attributes Architecture
We explicitly separate attributes at compile time:
// Exodra approach - explicit and fast
<div
static={{ id: 'container', class: 'box' }}
bindable={{ onClick: handler }}
bindableList={{ items: list }}
/>
Zero Runtime Checks
With Three Attributes, the runtime knows exactly what to do:
// Static attributes - set once, never check again
if (attributes.static) {
Object.assign(element, attributes.static);
}
// Bindable attributes - set up reactivity
if (attributes.bindable) {
setupBindings(element, attributes.bindable);
}
// Lists - optimize for array operations
if (attributes.bindableList) {
setupListBinding(element, attributes.bindableList);
}
No type checking. No guessing. No overhead.
Performance Impact
We don't ship a "runtime-type-checking" variant of Exodra to A/B against, so we won't quote a made-up "with checks vs without" delta. What we can show is how the architecture holds up against other reactive frameworks.
In the project's benchmark suite (npm run bench), rendering a large tree — the
case that actually does work at a measurable scale — Exodra lands in the top
tier: as fast as the fastest (neck‑and‑neck with Solid, well ahead of React and
Svelte) without a memory premium. Full numbers, including retained heap and
honest caveats, live on the dedicated Benchmarks page — with
the fair print that Solid's memory footprint is actually a touch smaller than
ours. We're not claiming a permanent "fastest" crown from our own harness.
Fine-grained updates (a single signal or list op) complete in sub-millisecond time for both Exodra and Solid — below the timer's resolution — so we treat those as a tie, not a headline multiplier.
Caveats worth stating plainly: this is Exodra's own harness (headless Chromium
via Playwright), not an independent benchmark, and numbers move with hardware and
framework versions. Run npm run bench and see for yourself. The point isn't "we
win every micro-benchmark" — it's that pre-separating props at compile time
removes a class of per-update work, and that shows up on real render workloads.
Why Not Just Use a Compiler?
"Why not let a compiler figure this out?"
We do! Our Babel plugin transforms JSX into Three Attributes. But here's the key: the runtime still knows the separation.
Other frameworks compile to a mixed format and still need runtime checks. We compile to a pre-separated format that needs zero runtime decisions.
Developer Experience
Yes, it's more verbose. But it's also:
- Explicit - You know exactly what's reactive and what's not
- Predictable - No surprising re-renders from static props
- Debuggable - Clear separation in DevTools
- Fast - Measurably, significantly faster
The Philosophy
We believe developers should control performance, not hope for it. By making the cost visible, we make it avoidable.
Every bindable prop is a conscious choice. Every static prop is a performance win. Every bindableList is optimized for its specific use case.
Real-World Impact
We won't quote before/after figures from a "production app" you can't re-run — that's how you end up trusting invented numbers. What you can run is the benchmark suite: npm run bench renders a large tree, and Exodra lands in the top tier in our harness — on par with Solid, ahead of Svelte and React (hardware/version dependent). The architectural claim doesn't hinge on the exact figure: pre-separating props at compile time removes a category of per-update work, and that shows up on real render workloads.
Conclusion
The Three Attributes Architecture isn't about following trends or making things "simple." It's about giving developers the tools to build genuinely fast applications.
We didn't want to drag type checking everywhere. So we didn't. We separated attributes at the architecture level, eliminated runtime overhead, and gave you maximum performance.
The cost of clarity is verbosity. The reward is speed.
