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.
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.
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:
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.
Hows this different from htmx?
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