A Reactive JavaScript Framework

2 pointsposted 8 hours ago
by kwy404

2 Comments

bitpush

8 hours ago

Hows this different from htmx?

kwy404

8 hours ago

I've been building a JavaScript framework called *Voodoo.js*. I'd rather get torn apart in the comments than hear that it looks cool.

It started with one question: how much JavaScript do we really need for everyday interactive interfaces?

For a lot of apps I don't want a component tree, a bundler, five libraries and state managed elsewhere, all wired back into HTML. Sometimes I just want to keep writing HTML.

## The 30-second version

```html <div v-data="{ count: 0 }"> <button @click="count--">-</button> <strong>{ count }</strong> <button @click="count++">+</button> </div> ```

No `querySelector`. No `addEventListener`. No Virtual DOM. Voodoo observes the real DOM and connects HTML directives to a fine-grained reactive system built on `Proxy`.

## Why another JS framework?

Voodoo isn't trying to replace React, Vue or Svelte. The target is projects where the server already renders the HTML — Laravel, Django, Rails, PHP, Go templates — plus small frontends where a full SPA is overkill.

Philosophically it sits near Alpine.js, HTMX and petite-vue. The difference I was chasing: HTTP, forms, validation, state, components, routing and reactivity should feel like one system, not five glued together.

## Declarative HTTP

Instead of wiring a button by hand:

```js button.addEventListener('click', async () => { if (!confirm('Delete user?')) return await fetch('/api/users/42', { method: 'DELETE' }) showToast('User deleted') }) ```

You declare the intent:

```html <button v-delete="/api/users/42" v-confirm="Delete this user?" v-toast-success="User deleted"> Delete </button> ```

Forms work the same way:

```html <form v-submit="/api/users" v-validate v-toast-success="User saved"> <input name="email" type="email" required> <button type="submit">Save</button> </form> ```

The goal isn't to remove JavaScript. It's to stop writing JavaScript that only restates what the HTML already says. When you need more, plain JS is right there.

## No `eval()`, no `new Function()`

The lazy way to implement `@click="count++"` is `eval()`. Voodoo does neither: expressions go through a real engine — Lexer → Pratt Parser → AST → Interpreter — parsed and interpreted by Voodoo itself, so the framework controls what syntax is supported and what expressions can reach.

## Fine-grained reactivity

The reactive layer uses `Proxy`, tracking dependencies per object and per property. No Virtual DOM diff — the real DOM is patched directly. The API should feel familiar:

```js reactive() ref() computed() watch() watchEffect() effectScope() toRaw() markRaw() ```

It runs from a plain `<script>` tag with no build step, or as ESM: `import { reactive } from 'voodoojs/reactivity'`.

## Where it fits

Server-rendered HTML + Voodoo.js → reactive interface. In Laravel:

```blade @foreach($users as $user) <button v-delete="/users/{{ $user->id }}" v-confirm="Delete user?"> Delete </button> @endforeach ```

The backend keeps rendering the app. No separate frontend application.

## The honest part

What began as an experiment grew into reactivity, directives, components, stores, HTTP, forms, validation, routing, i18n, charts, devtools and a CLI. That growth is now the problem. It doesn't need more features — it needs stability: security hardening, parser edge cases, tests, API stability, benchmarks, docs.

I'd rather ship a small set of features people can trust in production than a huge one nobody can.

## What I'm asking for

If you work with Alpine.js, HTMX, Vue, petite-vue, Stimulus or Livewire:

1. What would stop you from using this? 2. Which parts of the API feel intuitive? 3. Which feel unnecessary? 4. What would you need before calling it production-ready?

If something looks questionable in the architecture, API or security model, tell me. That's the feedback I need.

*GitHub:* https://github.com/kwy404/Voodoo.js