js-helper-vanilla
v1.0.0
Published
A tiny reactive state & component loader library for vanilla JS
Downloads
4
Maintainers
Readme
⚡ Mini Reactive Framework + Component Loader
This project demonstrates a vanilla JavaScript reactive state system with reusable HTML components.
It’s similar in spirit to React/Vue, but tiny, dependency-free, and runs in the browser.
📂 Project Structure
/project
│
├── functions.js # Core reactive library
├── mini.html # Demo app (E-commerce product manager)
└── component.html # Product card component template🚀 Features
- 🔄 Reactive state (
useState) — update UI when values change. - 🌀 Effects (
useEffect) — run code automatically when state changes. - 🏬 Global Store (
createStore) — manage app-wide state. - 🎨 Component loader (
useComponent) — load.htmltemplates with placeholders like{{product.name}}. - ✍ Two-way data binding (
bindInputToState) — sync form inputs with state. - 🛠 DOM helpers (
useSelect,useMultiSelect,createMultiUIUpdater).
🧩 Component Syntax
A component is just an .html file with placeholders inside {{ ... }}.
Example component.html:
<div class="bg-white rounded-xl shadow-lg overflow-hidden transform hover:scale-105 transition-transform duration-300">
<img src="{{product.image}}" loading="lazy" alt="{{product.name}}" class="w-full h-64 object-cover" />
<div class="p-6">
<h3 class="text-xl font-semibold mb-2 text-gray-800">{{product.name}}</h3>
<p class="text-gray-600 mb-4">{{product.description}}</p>
<div class="flex items-center justify-between">
<span class="text-2xl font-bold text-indigo-600">{{product.priceFixed}}</span>
<button data-on-click="buyNow"
class="px-4 py-2 bg-indigo-500 text-white rounded-full hover:bg-indigo-600 transition-colors">
Buy Now
</button>
</div>
</div>
</div>Placeholders like {{product.name}} will be replaced with real data.
🧪 How useComponent Works
import { useComponent } from "./functions.js";
async function init() {
const productCard = await useComponent("./component.html");
// Render component as string
console.log(productCard.asString({
product: { name: "Demo", priceFixed: "$10", image: "demo.png", description: "Example" }
}));
// Render component as a DOM node
const card = productCard.asDiv({
product: { name: "Demo", priceFixed: "$10", image: "demo.png", description: "Example" }
});
document.body.appendChild(card);
}
init();📖 Core API Guide
🔄 useState(initialValue, onUpdate?)
Creates reactive state.
const [count, setCount] = useState(0);
setCount(5); // updates state
console.log(count.value); // 5🌀 useEffect(effectFn, [dependencies])
Run effects when state changes.
useEffect(() => {
console.log("Count changed:", count.value);
}, [count]);🏬 createStore(initialState)
Global key/value reactive store.
const store = createStore({ toast: null });
store.setState("toast", "Hello!");
store.subscribe("toast", val => console.log(val));🎨 useComponent(path)
Loads HTML components with {{placeholders}}.
✍ bindInputToState(selector, state)
Binds an <input> to a reactive state.
const [name, setName] = useState("");
bindInputToState("#name-input", name);✅ Sample Project: To-Do List
Let’s build a To-Do List using components.
1. todo-component.html
<li class="flex items-center justify-between bg-white shadow p-4 rounded-lg mb-2">
<span>{{todo.text}}</span>
<button data-on-click="remove"
class="bg-red-500 text-white px-3 py-1 rounded-lg hover:bg-red-600">
Remove
</button>
</li>2. todo.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>To-Do List</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100 p-6">
<div class="max-w-md mx-auto bg-white p-6 rounded-lg shadow">
<h1 class="text-2xl font-bold mb-4">To-Do List</h1>
<form id="todo-form" class="flex space-x-2 mb-4">
<input id="todo-input" type="text" placeholder="New task..."
class="flex-1 border rounded p-2" required />
<button class="bg-indigo-500 text-white px-4 rounded">Add</button>
</form>
<ul id="todo-list"></ul>
</div>
<script type="module">
import { useState, useEffect, useSelect, useComponent, bindInputToState } from "./functions.js";
const [todos, setTodos] = useState([]);
const [inputValue, setInputValue] = useState("");
let todoComponent = null;
async function init() {
todoComponent = await useComponent("./todo-component.html");
renderTodos();
}
function renderTodos() {
const list = useSelect("#todo-list");
list.innerHTML = "";
todos.value.forEach((t, i) => {
const item = todoComponent.asDiv({ todo: { text: t } });
// attach remove button
item.querySelector("[data-on-click='remove']")
.addEventListener("click", () => {
setTodos(prev => prev.filter((_, idx) => idx !== i));
});
list.appendChild(item);
});
}
useEffect(renderTodos, [todos]);
// Bind input
bindInputToState("#todo-input", inputValue);
// Handle form
useSelect("#todo-form").addEventListener("submit", e => {
e.preventDefault();
if (inputValue.value.trim()) {
setTodos(prev => [...prev, inputValue.value.trim()]);
setInputValue(""); // clear input
}
});
init();
</script>
</body>
</html>🎯 Running the To-Do List
- Place
functions.js,todo.html, andtodo-component.htmlin the same folder. - Serve with a local static server (needed for
fetch):
npx serve .You now have a fully working To-Do List app with reusable components 🚀.
📌 Notes
- Components must be served over HTTP(S) (not
file://) because offetch. - Placeholders support nested paths like
{{product.name}}. - This system is intentionally simple — no virtual DOM, just direct DOM manipulation.
📜 License
MIT — Free to use and modify.
