Skip to main content

Command Palette

Search for a command to run...

JavaScript and the Dream of Controlling Reactivity

Updated
4 min read
JavaScript and the Dream of Controlling Reactivity

🌱 The Journey Begins

About five years ago, I shared a deeply personal approach to building web components — one rooted in Web Components and custom elements.

My idea was simple:
Define a new element, give it its own behavior, and interact directly with it — without re-rendering the entire DOM.

If you’ve ever opened YouTube → View Source, you’ll see how they structure their custom elements and attach isolated logic to each.
That’s what inspired me when I started.

So I built a tiny “engine” that allowed interaction with each element independently — minimizing DOM reflows, maximizing control.
The goal was clear: maximum control, minimal interference.

🤔 When Doubt Creeps In

At first, it all seemed to work perfectly.
But as projects grew, I faced an inevitable truth:

Direct DOM manipulation — while powerful — isn’t always scalable or optimal.

When I needed to extend or reuse components, I found myself repeating a lot of manual steps.
That made me question:

“Is there a way to keep my control philosophy, but also benefit from what modern JS frameworks offer?”

🔁 Rewriting with a New Philosophy

I started experimenting again — combining my own understanding with the strengths of existing frameworks.

When I learned about Virtual DOM, I saw how it solved certain problems — but it wasn’t what I truly needed.
I wanted something simpler.

And then a new philosophy emerged:

Write less. Do more.

My old process was:

  1. Mark the DOM.
  2. Use JS to find and bind elements.
  3. Write interactive logic directly on them.

It gave me 100% behavioral control — but at a high cost in time and maintainability.

🧩 The Solutions I Studied

I explored several libraries with similar philosophies:

  • Alpine.js – lightweight, easy to integrate, HTML-like syntax.
  • Web Components – standards-based, framework-free, but verbose.
  • HTMX – minimal JavaScript, great for server-driven UI.
  • AngularJS 1.x – early reactive pioneer, but too heavy for small features.
  • Petite-vue – a compact version of Vue, lovely but still limited.

Each had its strengths, yet I still longed for something smaller, tighter, more controllable.

⚡ Discovering Proxy and My First Reactive Engine

A turning point came last year, while developing a data table component for a client.
To optimize data updates, I discovered JavaScript Proxy — a surprisingly elegant tool for creating reactive data without relying on a complex virtual DOM.

Proxy felt smooth.
But then, the same old pain returned: every new feature meant rewriting parts of the engine from scratch.
The dev loop became: build → fix → rewrite → repeat.

So I decided to go deeper — to build my first true reactive engine, designed around how I naturally structure components.

🚫 Why I Didn’t Choose Mainstream Frameworks

I didn’t go with React, Angular, or Vue (full) because:

  • They were too heavy for my needs.
  • Not optimized for SSR (Server-Side Rendering) in my workflow.
  • They often required rewriting backend logic — I use Golang, and I didn’t want that friction.

jQuery still survives today because it solves practical problems quickly.
But I needed something even lighter, modern, and controllable.

🧠 The Development Philosophy: Micro Component

I imagine Micro Components like Microservices:

  • Each component does exactly one thing.
  • It can run independently.
  • It can hydrate anywhere, anytime.

This lets me embed mini JavaScript apps into mobile or web environments — without affecting other modules.
Since SEO is already handled by the server, I can focus purely on enhancing user experience on the client.

⚙️ Example: How It Works

<div
  data-kitmodule-component=”counter”
  data-counter-state=”count: 0”
>
  <input data-counter-model=”count” />
  <button data-counter-event=”click:count++”>+</button>
  <span data-counter-bind=”count”></span>
</div>

Explanation:

  • data-kitmodule-component — identifies the component.
  • data-counter-state — declares the initial state.
  • data-counter-model — two-way data binding with input.
  • data-counter-event — attaches event handlers directly via attributes.
  • data-counter-bind — updates the DOM when the state changes.

The result:
A lightweight, framework-free, yet fully reactive component.

🌸 What I Learned

This journey taught me that simplicity is often the ultimate sophistication.
Reactive doesn’t have to mean Virtual DOM or complex syntax.

Sometimes, all you need is:

  • a small engine,
  • readable, HTML-based syntax,
  • and a sprinkle of smart JavaScript.

In the next part, I’ll share the detailed architecture of this reactive engine
how it parses expressions, binds events, and syncs state with the DOM without using eval or Function constructors — ensuring CSP safety and XSS protection.

open: https://github.com/kitmodule/kitjs

📝 NOTES

  • Originally written in 2025, reposted with updates.
  • AI-powered translation from Vietnamese.
  • Read the original Vietnamese version here: [LINK_TO_ORIGINAL]

☕ More about me

Thanks for reading Huỳnh Nhân Quốc’s article! Subscribe for free to receive new posts and support my work.

More from this blog

I

Indie Hacker .Work

46 posts

👨‍💻 Dreamy indie-stack developer 🚀 Open source @ github.com/kitmodule ⚡ Golang + Javascript enthusiast 🤖 Work engine @ github.com/kitwork