npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

js-helper-vanilla

v1.0.0

Published

A tiny reactive state & component loader library for vanilla JS

Downloads

4

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 .html templates 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

  1. Place functions.js, todo.html, and todo-component.html in the same folder.
  2. Serve with a local static server (needed for fetch):
npx serve .
  1. Open http://localhost:3000/todo.html.

You now have a fully working To-Do List app with reusable components 🚀.


📌 Notes

  • Components must be served over HTTP(S) (not file://) because of fetch.
  • 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.