Overview
A lightweight reactive UI library.
Installation
Section titled “Installation”npm install @hellajs/core
Counter Example
Section titled “Counter Example”import { html, mount, signal } from "@hellajs/core";
// Ergonomic element proxiesconst { div, button, span } = html;
// Create reactive state OUTSIDE component functionsconst count = signal(0);
// Update reactive state OUTSIDE component functionsconst setCount = (changeBy) => count.set(count() + changeBy);
// Define component functions that use signalsconst Counter = () => div( button({ onclick: () => setCount(-1) }, "-"), span(count()), button({ onclick: () => setCount(+1) }, "+"), );
// Mount reactive componentsmount(Counter, "#app");
💥 Important: Signal Placement
Section titled “💥 Important: Signal Placement”Always define signals outside component functions
Unlike other libraries, reactive state and functions that update state MUST be defined OUTSIDE of the component function. Signals defined inside component functions won’t maintain their values between renders or trigger reactive updates.
Use context
if you need to create isolated state.
import { html, context, signal } from "@hellajs/core";
const { div } = html;
// ✅ CORRECT: Signals defined outside component functionconst ctx = context("user"); // Optional contextconst count = ctx.signal(0);const setCounter = (changeBy) => count.set(count() + changeBy)
const Counter = () => div( button({ onclick: () => setCounter(-1) }, "-"), span(count()), button({ onclick: () => setCounter(+1) }, "+"), );
ctx.mount(Counter, "#app");
// ❌ INCORRECT: Signals would be recreated on each render and lose reactivityconst Counter = () => { // This will not work as expected const count = signal(0); const setCounter = (changeBy) => count.set(count() + changeBy)
return div( button({ onclick: () => count.set(count() - 1) }, "-"), span(count()), button({ onclick: () => count.set(count() + 1) }, "+"), );}
Reactive State
Section titled “Reactive State”import { computed, effect, signal } from "@hellajs/core";
// Signals - reactive state containersconst user = signal("guest");const preferences = signal({ darkMode: false, notifications: true});
// Reading signalsconsole.log(name()); // "guest"console.log(preferences().darkMode); // false
// Computed signalsconst username = computed(() => { // Automatically tracks dependency on user() return user() === "guest" ? "Guest User" : user();});
const theme = computed(() => { // Automatically tracks dependency on preferences().darkMode return preferences().darkMode ? "dark-theme" : "light-theme";});
// Runs immediately and when any dependency changesconst cleanup = effect(() => { document.body.className = theme(); document.title = `${username()} (${counter()})`;
console.log(`Updated UI: user=${username()}, theme=${theme()}, count=${counter()}`);});
// These changes will trigger the effectuser.set("bob");preferences.update(p => ({ ...p, darkMode: true }));
console.log(username()); // "bob"console.log(theme()); // "dark-theme"
// Clean up when donecleanup();
// Changes after cleanup don't trigger effectsname.set("alice");
DOM Manipulation
Section titled “DOM Manipulation”Elements
Section titled “Elements”import { html } from "@hellajs/core";
const { div, h1, p, ul, li } = html;
// Simple elementsconst header = h1("Hello World");
// With attributes and eventsconst actionButton = button({ className: "primary", disabled: false, onclick: () => console.log("clicked"),}, "Click Me");
// Nested structureconst content = div( { className: "content" }, h1("My App"), p("Welcome to my application"), ul( li("Item 1"), li("Item 2"), ),);
Fragments
Section titled “Fragments”import { html } from "@hellajs/core";
const { tr, td, $ } = html;
// Create multiple elements without a wrapperconst tableRows = $( tr(td("Row 1, Cell 1"), td("Row 1, Cell 2")), tr(td("Row 2, Cell 1"), td("Row 2, Cell 2")),);
Components
Section titled “Components”Components are just functions that return virtual DOM nodes:
import { html, signal, mount } from "@hellajs/core";
const { div, h2, input, button } = html;
// State must be defined outside the componentconst username = signal("");
// State update functions must also be outside the componentconst handleSubmit = () => { console.log(`Submitting: ${username()}`);};
// Component that uses the external stateconst UserForm = () => div({ className: "form" }, h2("User Registration"), input({ value: username(), oninput: (_, el) => username.set((el as HTMLInputElement).value), placeholder: "Enter username", }), button({ onclick: handleSubmit }, "Submit"), )
// Mount a component with reactive updatesmount(UserForm, "#registration");