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

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: