I Built a UI Framework That Doesn’t Use a Virtual DOM
Introducing Zenith
Over the last few years, I’ve worked extensively with React, Vue, Next, Nuxt, Svelte, and compiler-adjacent tooling. Each of them solves real problems — but they also left me with a recurring question:
What if the compiler already knew everything before runtime ever existed?
That question is what led me to build Zenith.
Zenith is a compiler-first UI framework that intentionally does not use a Virtual DOM, runtime diffing, or reactive boundaries. Not because those tools are bad — but because I wanted to explore what becomes possible when certainty replaces reconciliation.
This post is an announcement, not a sales pitch. Zenith is different by design, and that difference is intentional.
Why I Started Building Zenith
Most modern UI frameworks share a common assumption:
UI updates are a runtime problem.
State changes, components re-run, virtual trees are recreated, and the framework figures out what changed after the fact.
Zenith flips that assumption:
UI updates are a compile-time problem.
Instead of asking “what changed?” at runtime, Zenith asks:
“What could ever change?”
…and answers that before runtime exists.
Once that answer is known, there’s nothing left to diff.
What Zenith Is (and Isn’t)
Zenith is:
- Compiler-driven
- Structural by default
- Predictable and explicit
- Minimal at runtime
Zenith is not:
- A Virtual DOM framework
- A reactive runtime
- A template engine
- A drop-in replacement for React or Vue
It’s a different axis entirely.
A Simple Counter Example
Let’s start with something familiar.
React
function Counter() {
const [count, setCount] = useState(0)
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
)
}
This works great — but under the hood:
- The component function re-executes
- A new Virtual DOM tree is created
- React diffs old vs new
- Then updates the DOM
All decisions happen at runtime.
Zenith
Zenith enforces a strict separation:
- Behavior lives in
<script> - Markup only declares bindings
- No inline expressions
- No directives
- No runtime interpretation
<script>
state count = 0
function increment() {
count++
}
</script>
<button onclick="increment">
Count: {count}
</button>
That’s it.
What the Compiler Does
At compile time, Zenith:
- Resolves
countas a state cell - Resolves
incrementas a function reference - Determines
{count}affects a single text node -
Emits exact instructions:
- One DOM event listener
- One direct DOM text update
At runtime:
- The function runs
- State mutates
- The known DOM node updates directly
There is no re-render path.
There is no diffing step.
There is nothing to guess.
Why {} Exists in Zenith
In Zenith, {} does not mean “evaluate an expression.”
It means:
“Bind this node to a compiler-tracked value.”
That’s why these are invalid:
{count + 1}
{someFunction()}
onclick={count++}
Those require runtime evaluation — and Zenith rejects them at compile time.
The compiler must be certain, or it fails.
Components in Zenith Are Structural
One of Zenith’s core constraints is:
Components do not introduce behavior or reactivity.
They are purely structural transforms.
- No lifecycle hooks
- No hidden scopes
- No reactive boundaries
This allows:
- Slot content to retain its original scope
- State identity to remain intact
- The compiler to reason globally
This constraint is not accidental — it’s what makes the model possible.
Why No Virtual DOM?
The Virtual DOM is an elegant solution to a hard problem.
Zenith avoids the problem entirely.
VDOM frameworks:
- Recompute → then optimize
- Recover from uncertainty
Zenith:
- Prevents uncertainty
- Emits intent, not guesses
That’s not “better” — it’s different by construction.
Current Status
Zenith is:
- Actively developed
- Compiler-driven (Rust)
- Early, opinionated, and evolving
It’s not production-ready yet — and that’s okay.
Right now, Zenith is about exploring:
- What a compiler can guarantee
- How much runtime can be eliminated
- What UI looks like without reactive boundaries
Why I’m Sharing This Now
Zenith started as a question.
It grew into a compiler.
Now it’s a framework.
I’m sharing it early because:
- I value architectural discussion
- I want ideas challenged
- And I believe UI tooling still has unexplored space
If you’re curious, skeptical, or opinionated — that’s exactly the audience I want to hear from.
Final Thought
Zenith isn’t trying to replace React or Vue.
It’s asking a different question:
What if the compiler already knew everything?
Thanks for reading — more to come.


Top comments (0)