Hello HN,

I've been working on Eleva.js, a minimalist frontend framework that just hit v1.0.0.

What it is: A 2.3KB (gzipped) vanilla JavaScript framework with signal-based reactivity and direct DOM patching.

Why I built it: I wanted something between writing raw DOM manipulation and using React/Vue. No JSX, no compiler, no virtual DOM — just native template literals and a simple reactivity primitive.

The mental model is intentionally small:

  const app = new Eleva("App");

  app.component("Counter", {
    setup({ signal }) {
      const count = signal(0);
      return { count, inc: () => count.value++ };
    },
    template: (ctx) => `
      <p>${ctx.count.value}</p>
      <button @click="inc">+</button>
    `
  });

  app.mount(document.body, "Counter");

Technical choices:

- Signals for reactivity (similar to Solid/Preact signals) - Direct DOM diffing instead of virtual DOM - Render batching via queueMicrotask - No build step required — works with native ES modules - ~0.5KB/row memory overhead in Chrome benchmarks

Trade-offs:

- No SSR yet (client-side only) - Template strings aren't as composable as JSX - Smaller ecosystem than established frameworks

Links:

- Docs: https://elevajs.com - GitHub: https://github.com/TarekRaafat/eleva - npm: npm install eleva

Happy to answer questions about the implementation or design decisions.

I know this is a thing of taste, but have you considered a syntax closer to SolidJS's approach? Something that feels a bit more vanilla JavaScript, where signals are just tuples with getter/setter and you use JSX instead of template strings and components are just plain functions?

For comparison, here's how this example would look:

  import { render } from "solid-js/web";
  import { createSignal } from "solid-js";

  function Counter() {
    const [getCount, setCount] = createSignal(0);
    return <button onClick={() => setCount(getCount() + 1)}>{getCount()}</button>;
  }
  render(() => <Counter />, document.getElementById("app"));