DEV Community

Cover image for A reactive JS framework for server-rendered apps — no Virtual DOM, no eval(), no build step required. Looking for critical feedback.
kwy404
kwy404

Posted on

A reactive JS framework for server-rendered apps — no Virtual DOM, no eval(), no build step required. Looking for critical feedback.

I've been building a JavaScript framework called Voodoo.js, and 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 actually need to write for everyday interactive interfaces?

For a lot of apps I don't want to build a component tree, configure a bundler, install five libraries, wrap the API, manage state somewhere else, and then wire it all back into HTML.

Sometimes I just want to keep writing HTML.

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


The 30-second version

A reactive counter:

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

  <strong>{ count }</strong>

  <button @click="count++">+</button>
</div>
Enter fullscreen mode Exit fullscreen mode

No querySelector. No addEventListener. No innerHTML. 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?"

Fair question. It's the first one I'd ask too.

Voodoo isn't trying to replace React, Vue or Svelte. The target is different: projects where the server already renders the HTML.

Laravel, Django, Rails, PHP, ASP.NET, Node, Go templates — plus small frontends where a full SPA is overkill.

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


Declarative HTTP

This is the part I enjoy using most.

Instead of wiring a button by hand:

button.addEventListener('click', async () => {
  if (!confirm('Delete user?')) return

  await fetch('/api/users/42', { method: 'DELETE' })

  showToast('User deleted')
})
Enter fullscreen mode Exit fullscreen mode

You declare the intent:

<button
  v-delete="/api/users/42"
  v-confirm="Delete this user?"
  v-toast-success="User deleted">
  Delete
</button>
Enter fullscreen mode Exit fullscreen mode

The goal isn't to make JavaScript disappear. It's to stop writing JavaScript that only restates what the HTML already says.


Forms

Same idea:

<form
  v-submit="/api/users"
  v-validate
  v-toast-success="User saved">

  <input name="email" type="email" required>

  <button type="submit">Save</button>
</form>
Enter fullscreen mode Exit fullscreen mode

For most CRUD screens, that's the whole thing. When you need more, plain JavaScript is still right there. Voodoo is meant to be progressive, not restrictive.


No eval(), no new Function()

Take this:

<button @click="count++">
Enter fullscreen mode Exit fullscreen mode

The lazy implementation is new Function(expression) or eval(expression). Voodoo does neither.

There's a full expression engine inside:

Lexer → Pratt Parser → AST → Interpreter
Enter fullscreen mode Exit fullscreen mode

Expressions are parsed and interpreted by Voodoo itself. That gives the framework control over which syntax is supported and what the expression environment can touch — and honestly it's the most interesting engineering in the project.


Fine-grained reactivity

The reactive layer uses Proxy, tracking dependencies per object and per property:

reactive object
    ↓ property accessed
dependency tracked
    ↓ property changed
only dependent effects run
Enter fullscreen mode Exit fullscreen mode

No Virtual DOM diff between updates. The real DOM is patched directly.

The reactivity package exposes a familiar surface:

reactive()   ref()        computed()
watch()      watchEffect() effectScope()
toRaw()      markRaw()
Enter fullscreen mode Exit fullscreen mode

If you've used a modern reactive framework, none of this should surprise you.


Works with or without a build step

Drop it in:

<script src="voodoo.js"></script>
Enter fullscreen mode Exit fullscreen mode
<div v-data="{ open: false }">
  <button @click="open = !open">Toggle</button>

  <div v-show="open">Hello DEV Community 👋</div>
</div>
Enter fullscreen mode Exit fullscreen mode

Or import it like anything else:

import { reactive } from 'voodoojs/reactivity'
Enter fullscreen mode Exit fullscreen mode

Both audiences matter to me: the "paste a script tag" crowd and the "give me proper ESM" crowd.


Where it fits

Server-rendered HTML  +  Voodoo.js  →  Reactive interface
Enter fullscreen mode Exit fullscreen mode

A Laravel view, for example:

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

The backend keeps rendering the app. Voodoo adds the interactive layer. No separate frontend application.


The honest part

What began as an experiment grew into a large framework: reactivity, directives, components, stores, HTTP, forms, validation, persistence, routing, i18n, UI utilities, animation, drag and drop, charts, streaming, devtools, and a CLI.

That growth is now the problem. The project doesn't need another hundred features — it needs stability.

Current focus:

  • 🔒 Security hardening
  • 🧩 Parser edge cases
  • ✅ Test coverage
  • 📐 API stability
  • 📦 Bundle modularity
  • ⚡ Performance benchmarks
  • 📚 Documentation
  • 🚀 Package and release quality

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, Livewire, server-rendered apps, or plain JS — I want your read on this:

  1. What would stop you from using something like Voodoo.js?
  2. Which parts of the API feel intuitive?
  3. Which parts feel unnecessary?
  4. What would you need to see before calling it production-ready?

And if you like working on JS internals, there's real work available: parsers, reactivity, testing, browser compatibility, TypeScript, performance, security, docs, components.

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

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

Top comments (2)

Collapse
 
dedaldinodev4 profile image
Dedaldino Daniel

Good idea !!

Collapse
 
kwy404 profile image
kwy404

Thank you for checking out this project.If you found this repository helpful or learned something new, please consider leaving a star. Your support increases the project's visibility and motivates me to keep building and improving it.