Every few years someone asks:
"Do we really need another JavaScript framework?"
The honest answer is:
Probably not.
But building one is one of the best ways to truly understand how modern frontend frameworks work.
Over the last several months I've been building Jacaré, an experimental compile-time JavaScript framework.
The objective wasn't to replace existing tools.
The objective was to understand how everything works internally by designing every major subsystem from scratch.
That meant building:
- A compiler
- A reactive runtime
- A rendering engine
- A component model
- Navigation
- Forms
- SSR
- DevTools
- CLI tooling
This article isn't about announcing a framework.
It's about sharing what I learned while building one.
Why build a framework?
Most frontend developers use frameworks every day.
Very few have the opportunity to understand what actually happens behind the scenes.
Questions like these kept coming back:
- How does a template become JavaScript?
- How are reactive dependencies tracked?
- Why do some runtimes stay incredibly small?
- How do frameworks update only what changed?
- How can rendering stay predictable while remaining fast?
Eventually I stopped reading about these problems.
I started implementing them.
The first architectural decision
One of the earliest decisions was choosing compile-time rendering.
Instead of parsing templates inside the browser, Jacaré compiles .jcr files into optimized JavaScript during the build.
A simple component looks like this:
import { signal } from '@jacare/core'
const count = signal(0)
export <view>
<main>
<p>${count}</p>
<button
on-click=${() => count.update(n => n + 1)}
>
+1
</button>
</main>
</view>
The compiler transforms this into optimized DOM operations.
There is no runtime template parser.
The browser only executes JavaScript.
Fine-grained reactivity
The next challenge was designing the reactive system.
Instead of updating an entire component whenever state changes, each binding subscribes only to the signals it actually depends on.
Conceptually it looks like this:
Signal
│
▼
Binding
│
▼
DOM Node
When a signal changes:
- no component re-executes
- no template is interpreted again
- no unrelated DOM nodes are touched
Only the affected bindings update.
This design keeps updates deterministic while allowing the runtime to remain extremely small.
Why use a compiler?
A compiler can eliminate work before the application even reaches the browser.
Instead of interpreting templates during execution, the compiler generates specialized JavaScript tailored for each component.
That allows the runtime to focus almost entirely on state propagation and DOM updates.
Moving complexity from runtime to build time became one of the guiding principles of the project.
Building the runtime
One of my goals was keeping the runtime as small as possible.
Current runtime size:
~1.2 KB (gzip)
The small size wasn't the primary objective.
It was simply the result of pushing as much work as possible into the compiler.
The hardest problem wasn't rendering
Surprisingly, rendering wasn't the most difficult part.
Designing the reactive graph was.
Questions like:
- How should dependencies be represented?
- How should updates propagate?
- How should lists be reconciled?
- How should shared state work?
- How should effects be scheduled?
Those decisions ended up influencing almost every subsystem in the framework.
Current features
Today Jacaré includes:
- Compile-time rendering
- Fine-grained reactivity
- Direct DOM updates
- Incremental rendering
- SSR
- Navigation
- Forms
- DevTools
- Interactive API tutorial
- Online playground
- Component contracts
- Reactive CSS variables
- Native shared state
The project is still experimental, and there are many ideas I'd like to explore in future versions.
Things I would redesign today
Building a framework changes how you think about software architecture.
Some APIs that looked elegant at first became harder to maintain.
Other ideas that initially seemed complicated turned out to simplify the entire system later.
That's probably the biggest lesson from this project:
The first implementation is rarely the final architecture.
Every subsystem taught me something different.
I'd love technical feedback
If you're interested in:
- compiler design
- reactive runtimes
- incremental rendering
- DOM engines
- framework architecture
I'd genuinely appreciate your feedback.
GitHub
https://github.com/jacarejs/core
Interactive API tutorial
https://jacarejs.github.io/core/lab/
Online playground
https://jacarejs.github.io/core/studio/
I'm especially interested in criticism.
If you think an architectural decision could be improved—or completely redesigned—I'd love to hear why.
What's next?
Over the next few weeks I plan to write a series of articles covering the internals of the project, including:
- Building a reactive runtime
- Writing a template compiler
- Incremental DOM rendering
- Designing a component model
- Shared state architecture
- Compiler optimizations
- DevTools implementation
- Lessons learned from building a framework from scratch
Thanks for reading! 🐊
Top comments (0)