HellaJS - A Tiny Reactive Framework

In this article, we'll explore some core concepts by building a simple Todo app with HellaJS, which is: Fast: Direct DOM updates for optimal performance. Composable: Signals, computed values, effects, and stores. Lightweight: Zero dependencies, small bundle size. Declarative: Write UI with ergonomic tag functions. Simple: Works out of the box with a small learning curve. Tutorial: Building a Todo App Let's put these concepts into practice... 1. Setup State and Reactivity We'll use signals to manage the list of todos and the input value. import { signal, effect } from "@hellajs/core"; function Todo() { const todos = signal([]); const inputValue = signal(""); const todoCount = () => todos().length; const hasTodos = () => todoCount() > 0; const isDisabled = () => inputValue() === ""; effect(() => { console.log("Todo count:", todoCount()); }); // ...event handlers and UI will go here... } 2. Handle Input and Add Todos Define functions to update the input and add new todos. const setInput = (event) => { inputValue.set(event.target.value); }; const addTodo = () => { todos.set([...todos(), inputValue()]); inputValue.set(""); }; 3. Build the UI Use html, forEach, and show to declaratively build the UI. import { forEach, show, html } from "@hellajs/dom"; const { div, input, button, ul, li, p } = html; return div( p("Todo List (", todoCount, ")"), div( input({ type: "text", value: inputValue, oninput: setInput, }), button({ onclick: addTodo, disabled: isDisabled }, "Add"), ), show( hasTodos, ul(forEach(todos, (todo) => li(todo))), div("No todos yet!"), ), ); 4. Mount the App Finally, render the Todo app to the DOM. import { mount } from "@hellajs/dom"; mount(Todo, "#todo"); 5. Full Example Here's the complete Todo app: import { signal, effect } from "@hellajs/core"; import { forEach, show, html, mount } from "@hellajs/dom"; const { div, input, button, ul, li, p } = html; function Todo() { const todos = signal([]); const inputValue = signal(""); const todoCount = () => todos().length; const hasTodos = () => todoCount() > 0; const isDisabled = () => inputValue() === ""; effect(() => { console.log("Todo count:", todoCount()); }); const setInput = (event) => { inputValue.set(event.target.value); }; const addTodo = () => { todos.set([...todos(), inputValue()]); inputValue.set(""); }; return div( p("Todo List (", todoCount, ")"), div( input({ type: "text", value: inputValue, oninput: setInput, }), button({ onclick: addTodo, disabled: isDisabled }, "Add"), ), show( hasTodos, ul(forEach(todos, (todo) => li(todo))), div("No todos yet!"), ), ); } mount(Todo, "#todo"); Conclusion HellaJS makes it easy to build reactive, declarative web apps with minimal overhead. Its composable API and direct DOM updates provide both simplicity and performance. Try it out for your next project! Learn more: Docs GitHub Benchmark

Jun 20, 2025 - 03:50
 0
HellaJS - A Tiny Reactive Framework

In this article, we'll explore some core concepts by building a simple Todo app with HellaJS, which is:

  • Fast: Direct DOM updates for optimal performance.
  • Composable: Signals, computed values, effects, and stores.
  • Lightweight: Zero dependencies, small bundle size.
  • Declarative: Write UI with ergonomic tag functions.
  • Simple: Works out of the box with a small learning curve.

Tutorial: Building a Todo App

Let's put these concepts into practice...

1. Setup State and Reactivity

We'll use signals to manage the list of todos and the input value.

import { signal, effect } from "@hellajs/core";

function Todo() {
  const todos = signal([]);
  const inputValue = signal("");

  const todoCount = () => todos().length;
  const hasTodos = () => todoCount() > 0;
  const isDisabled = () => inputValue() === "";

  effect(() => {
    console.log("Todo count:", todoCount());
  });

  // ...event handlers and UI will go here...
}

2. Handle Input and Add Todos

Define functions to update the input and add new todos.

const setInput = (event) => {
  inputValue.set(event.target.value);
};

const addTodo = () => {
  todos.set([...todos(), inputValue()]);
  inputValue.set("");
};

3. Build the UI

Use html, forEach, and show to declaratively build the UI.

import { forEach, show, html } from "@hellajs/dom";
const { div, input, button, ul, li, p } = html;

return div(
  p("Todo List (", todoCount, ")"),
  div(
    input({
      type: "text",
      value: inputValue,
      oninput: setInput,
    }),
    button({ onclick: addTodo, disabled: isDisabled }, "Add"),
  ),
  show(
    hasTodos,
    ul(forEach(todos, (todo) => li(todo))),
    div("No todos yet!"),
  ),
);

4. Mount the App

Finally, render the Todo app to the DOM.

import { mount } from "@hellajs/dom";
mount(Todo, "#todo");

5. Full Example

Here's the complete Todo app:

import { signal, effect } from "@hellajs/core";
import { forEach, show, html, mount } from "@hellajs/dom";

const { div, input, button, ul, li, p } = html;

function Todo() {
  const todos = signal([]);
  const inputValue = signal("");

  const todoCount = () => todos().length;
  const hasTodos = () => todoCount() > 0;
  const isDisabled = () => inputValue() === "";

  effect(() => {
    console.log("Todo count:", todoCount());
  });

  const setInput = (event) => {
    inputValue.set(event.target.value);
  };

  const addTodo = () => {
    todos.set([...todos(), inputValue()]);
    inputValue.set("");
  };

  return div(
    p("Todo List (", todoCount, ")"),
    div(
      input({
        type: "text",
        value: inputValue,
        oninput: setInput,
      }),
      button({ onclick: addTodo, disabled: isDisabled }, "Add"),
    ),
    show(
      hasTodos,
      ul(forEach(todos, (todo) => li(todo))),
      div("No todos yet!"),
    ),
  );
}

mount(Todo, "#todo");

Conclusion

HellaJS makes it easy to build reactive, declarative web apps with minimal overhead. Its composable API and direct DOM updates provide both simplicity and performance. Try it out for your next project!

Learn more: