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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@magnumjs/micro-ui

v1.15.3

Published

A tiny reactive component system with declarative slots, keyed lists, reactive props, and DOM diffing.

Readme

📩 Micro UI - Reactive Component System

Coverage Docs: GitHub Pages npm version Build Status npm package minimized gzipped size npm downloads License: MIT Known Vulnerabilities

A minimalist reactive component library with support for state, props, named slots, refs, DOM diffing, shared context, and declarative event bindings.

✨ Features

  • createComponent() with internal state, setState, and refs
  • Props are read-only (passed in by parent)
  • Declarative rendering using template strings or render functions
  • Named and default slots (<slot name="..."> and data-slot="..." support)
  • Support for this.refs and lazy this.ref(name) inside components
  • Lifecycle hooks: onMount, onUnmount, onBeforeMount, onBeforeUnmount, onUpdate
  • Diffing DOM updates for performance with data-key="..."
  • Keyed list rendering with renderList() for efficient updates
  • DOM caching when render() returns null
  • Declarative event binding via on option (e.g. "click .btn") and data-action
  • Arguments support via data-args for cleaner templates
  • Built-in context pub/sub with shared() stores
  • Full unit test coverage

🚀 Getting Started

npm i @magnumjs/micro-ui

📄 PDF Guides

import { createComponent } from "@magnumjs/micro-ui";

const ClickCounter = createComponent({
  state: {
    count: 0,
  },
  render() {
    this.handleClick = () => {
      this.setState({ count: ++this.state.count });
    };

    return `
      <button data-action-click="handleClick">
        Count: ${this.state.count}
      </button>`;
  },
});

ClickCounter.mount("#app");

JSBin

🧩 Composability for Components

// Child component (function literal style)
const Child = createComponent(() => `<p>Hello from Child!</p>`);

// Sibling component with a callback prop
const Sibling = createComponent(({ props }) => `
  <button data-action-click="notifyParent">Notify Parent</button>
`, {
  on: {
    "click:notifyParent"() {
      if (typeof this.props.onNotify === "function") {
        this.props.onNotify();
      }
    }
  }
});

// Parent component calls Child() and Sibling() inline in render
const Parent = createComponent({
  state: { notified: false },
  render() {
    // Callback to rerender parent
    const handleNotify = () => {
      this.setState({ notified: true });
    };
    return `
      <div>
        ${Child()}
        ${Sibling({ onNotify: handleNotify })}
        <p>Sibling notified: ${this.state.notified}</p>
      </div>
    `;
  }
});

Parent.mount("#app");

JSBin

📡 Global Shared State with shared()

Create a shared state store with event-based updates:

import { shared } from "@magnumjs/micro-ui/utils";

const auth = shared("auth", { user: null });

auth.subscribe(console.log); // Logs current and future state

auth.emit("login", { user: "Tova" }); // auto-merges into state

You can on(event, fn) to subscribe to specific events (e.g. "login", "logout").

auth.on("logout", () => console.log("logged out"));
auth.emit("logout", { user: null });

⚡ Inline Actions with data-action and data-args

You can declaratively bind handlers in your template:

const Demo = createComponent(
  () => `
  <button data-action="sayHello" data-args='{"name":"Tova"}'>Hi</button>
`,
  {
    on: {
      "click:sayHello"({ args }) {
        alert(`Hello, ${args[0]}!`);
      },
    },
  }
);

🧬 Component Example

const MyCard = createComponent(
  ({ props: { title = "", children } }) => `
  <div class="card">
    <header data-slot="header">${title}</header>
    <main><slot></slot></main>
    <footer data-slot="footer">Default Footer</footer>
  </div>
`
);

✅ Mounting & Updating

MyCard.mount("#demo");
MyCard.update({
  children: {
    default: "<p>Hello world!</p>",
    footer: "<p>Custom footer here</p>",
  },
});

🔁 Internal State (DX)

Each component automatically has this.state and this.setState. Usage:

const Counter = createComponent(
  function () {
    const count = this.state.count ?? 0;

    return `<button>Count: ${count}</button>`;
  },
  {
    onMount() {
      this.setState({ count: 0 });
    },
    on: {
      "click button"(e) {
        this.setState((s) => ({ count: s.count + 1 }));
      },
    },
  }
);

🔌 Slots with Fallbacks

Named slots work with both <slot name="x"> and <div data-slot="x">.

const Card = createComponent(
  () => `
  <section>
    <header data-slot="title">Default Title</header>
    <main><slot></slot></main>
    <footer data-slot="footer">Default Footer</footer>
  </section>
`
);

🔁 Lifecycle Hooks

createComponent(() => "<p>Lifecycle</p>", {
  onBeforeMount() {
    // Called before initial mount (async supported)
  },
  onMount() {
    // Called after initial mount
  },
  onUpdate(prevProps) {
    // Called after update render
  },
  onBeforeUnmount(next) {
    // Delay unmount with callback or Promise or just sync
    setTimeout(() => next(), 100);
  },
  onUnmount() {
    // Final cleanup logic
  },
});

📖 Core API Docs

🧩 MicroUI Client Example

🧱 API

createComponent(renderFn, options)

// Object instance style: createComponent returns a component instance
const Comp = createComponent({
  render({ state }) {
    return state.show ? `
      <div data-ref="container">
        <span>${state.count}</span>
        <button data-ref="inc">+</button>
      </div>
    ` : null;
  },
  state: { count: 0, show: true },
  on: {
    "click [data-ref='inc']": ({ setState, state }) => {
      setState({ count: state.count + 1 });
    }
  },
  // Add lifecycle handlers directly on the instance
  onMount() {
    console.log("Mounted!");
  },
  onUpdate(prevProps) {
    console.log("Updated!");
  },
  onUnmount() {
    console.log("Unmounted!");
  }
});

// Instance can be called in literals, with toString override:
const html = `<section>${Comp({ show: true })}</section>`;

// Inline actions with data-action-event:
const Demo = createComponent({
  render() {
    return `<button data-action-click="sayHello" data-name="${}">Say Hi</button>`;
  },
  sayHello() {
    alert("Hello!");
  }
});

// Hooks: effect, state, context ..
// Compose your own hooks, e.g. useFetch
import { useEffect, useState, useContext } from "@magnumjs/micro-ui/hooks";

function useFetch(url) {
  const data = useState(null);
  useEffect(() => {
    fetch(url).then(res => res.json()).then(data.set);
  }, [url]);
  return data;
}

Instance Methods

  • Comp.mount(target) — Mount to target container
  • Comp.update(nextProps) — Update props and re-render
  • Comp.setState(nextState) — Trigger state update
  • Comp.unmount() — Cleanly unmount component
  • Comp.renderFn() — Returns the original component as String

Comp.el

Auto-populated with the Parent Node after mount.

Comp.refs

Auto-populated with [data-ref="name"] nodes after mount.

Comp.ref(name)

Lazy accessor for a single ref. Returns null after unmount.

Comp.props

Auto-populated with props from Comp.update(nextProps) before each render.

Comp.state

Auto-populated with state after setState.

DOM Caching on null

If render() returns null, the previous DOM is cached and restored if render() returns content again.

🔁 renderList(array, renderFn, keyFn?)

Renders keyed list efficiently:

import { renderList } from "@magnumjs/micro-ui/utils";

renderList(
  data,
  (item) => `<li>${item.label}</li>`,
  (item) => item.id
);

Auto-wraps each root tag with data-key for DOM diffing.

🧪 Testing

✅ Example Test Case

const Counter = createComponent(
  ({ state, setState }) => {
    return `
    <button data-ref="btn">${state.count}</button>
  `;
  },
  {
    state: { count: 0 },
    on: {
      "click [data-ref='btn']": ({ state, setState }) => {
        setState({ count: state.count + 1 });
      },
    },
  }
);

Mount and assert changes after click.


🤝 Contributing

Pull requests are welcome!


Built with ❤️ by developers who love simplicity.