DEV Community

Cover image for HellaJS - A Tiny Reactive Framework
omilli
omilli

Posted on

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...
}
Enter fullscreen mode Exit fullscreen mode

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("");
};
Enter fullscreen mode Exit fullscreen mode

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!"),
  ),
);
Enter fullscreen mode Exit fullscreen mode

4. Mount the App

Finally, render the Todo app to the DOM.

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

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");
Enter fullscreen mode Exit fullscreen mode

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:

Top comments (0)