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

@zoijs/core

v1.6.0

Published

Zoijs — a beginner-friendly, no-build, no-Virtual-DOM frontend framework. Plain HTML, CSS, and JavaScript.

Readme

Zoijs

A lightweight frontend framework you don't have to learn before you use it — plain HTML, CSS, and JavaScript, no JSX, no build step, no Virtual DOM.

Documentation · GitHub · npm

npm install @zoijs/core
import { html, mount, createState } from "@zoijs/core";

function Counter() {
  const count = createState(0);
  return html`<button onclick=${() => count.set(count.get() + 1)}>${() => count.get()}</button>`;
}
mount(Counter, "#app");

📚 Documentation

New here? Start at zoijs.dev (or the docs folder) — designed to get you productive in under 30 minutes.


Mission

Make building modern web applications feel as approachable as writing plain HTML, CSS, and JavaScript — so that any developer, on day one, can ship real software without first learning a framework.

The framework should disappear into the skills developers already have. The whole mental model is three verbs: write a function that returns html, put createState values in it, mount it.

Goals

  • Beginner-friendly — concepts you already know from vanilla JS/HTML/CSS.
  • No build step — runs from a single <script type="module">.
  • No Virtual DOM — fine-grained, direct DOM updates; only what changed is touched.
  • Minimal runtime — the browser does the heavy lifting (native <template>, cloneNode, events).
  • Secure by default — inert text rendering, URL-scheme guards, handler references (never strings), no eval.
  • Small & readable — a junior developer can read the source.

See docs/Phase-1-MVP-Spec.md for the full specification.

Setup

No build step is required — the framework is plain ES modules.

# from the framework/ directory

# run the counter example (serves the project root over http so ES modules resolve)
npm run dev
# then open: http://localhost:7310/examples/counter/   (keep the trailing slash)

# run the tests (DOM tests run automatically via jsdom)
npm test

Tips:

  • ES module imports need to be served over http://, not opened as a file:// path. npm run dev handles that.
  • Use the trailing slash on the example URL (/examples/counter/). Without it, some static servers resolve ./app.js against the wrong directory and the app won't load.

Testing

| Command | What it runs | |---|---| | npm test | Unit + DOM tests via jsdom (fast, no browser) | | npm run test:unit | Pure-logic tests only (no DOM) | | npm run test:types | TypeScript type-checks (tsc --noEmit) | | npm run test:browser | Real-browser tests in Chromium, Firefox, WebKit (Playwright) |

Browser tests live in browser-tests/ and run the example apps plus framework regressions against real engines. First-time setup downloads the browsers:

npm install
npx playwright install chromium firefox webkit
npm run test:browser

Playwright starts a static server automatically (npx serve on port 7310) — still no build step.

Browser support

Modern evergreen browsers. Verified automatically (Playwright) in:

| Browser | Engine | Status | |---|---|---| | Chrome / Edge | Chromium | ✅ tested | | Firefox | Gecko | ✅ tested | | Safari | WebKit | ✅ tested |

Relies on baseline-modern platform APIs: ES modules, <template>, TreeWalker, Proxy, queueMicrotask, replaceChildren, addEventListener, setAttributeNS. No IE support, no transpilation, no polyfills.

Public API

The whole framework is nine functions — learnable in one sitting and frozen for 1.x:

import {
  html, mount, each, boundary,
  createState, computed, effect,
  configure, onCleanup,
} from "@zoijs/core";
  • html — tagged template; parsed once, cached.
  • mount(component, target) — render a component; returns unmount().
  • each(itemsFn, keyFn, renderFn) — keyed list rendering (reuse / move / remove nodes).
  • boundary(child, fallback) — render-time error boundary: if child throws while building its markup, dispose the partial work and render fallback.
  • createState(value) — a reactive value (get / set / peek).
  • computed(fn) — a lazy, cached, value-gated derived value (get / peek).
  • effect(fn) — a side effect that re-runs when a value it reads changes; returns { dispose } and may return a per-run cleanup.
  • configure({ dev }) — toggle development warnings (default dev: true).
  • onCleanup(fn) — register teardown for a component or list item (timers, subscriptions).

Plus the ref binding (html\<input ref=${(el) => el.focus()} />``) — no export; it's a template attribute that hands you the rendered element.

Devtools (dev-only). A separate subpath, @zoijs/core/devtools, exposes a read-only inspection hook — attachInspector(inspector) and inspecting() — that @zoijs/devtools (or a browser extension) uses to observe the reactive graph. It's off by default, never instruments the hot read path, and is a no-op under configure({ dev: false }), so it costs production nothing and leaves the nine-function main surface unchanged.

See the Documentation site for the full guide, tutorials, and API reference.

TypeScript: ships type definitions (src/index.d.ts) for autocomplete and optional type-checking — JS-first, no build step required. createState<T>, computed<T>, and each<T> are generic. Type-check with npm run test:types.

What's built

  • Fine-grained text/attribute bindings — ${() => state.get()} updates one node in place; setup runs once (no re-render).
  • Native events, secure-by-default rendering (inert text, URL-scheme guards, no eval).
  • computed() derived values — lazy, cached, nestable, and value-gated (unchanged results don't wake downstream).
  • effect() side effects — re-run on change, with owner-scoped auto-disposal and a per-run cleanup.
  • boundary() render-time error boundary — a failing subtree shows a fallback instead of breaking mount.
  • each() keyed list reconciliation — minimal DOM moves; preserves focus / input / scroll on reorder.
  • Microtask batching, push-pull dependency tracking, owner-scoped cleanup (unmount and removed items dispose their subscriptions).
  • Production mode via configure({ dev: false }) — no build step.
  • Safety: self-triggering effects are warned + stopped; a throwing binding doesn't break others.

Out of scope (by design): router, CLI, plugins, SSR, global store, TypeScript-first setup, Virtual DOM.

Project Structure

framework/
  package.json
  README.md
  src/
    core/
      html.js        # html() — parse template into a cached blueprint
      mount.js       # mount() — entry point; instantiate + attach + cleanup
      renderer.js    # internal: bind dynamic slots, apply fine-grained updates
    reactivity/
      state.js       # createState() + internal dependency tracking
    utils/
      dom.js         # small native-DOM helpers
      security.js    # escaping, URL-scheme allowlist, attribute-name guards
    index.js         # public entry — re-exports html, mount, createState
  examples/
    counter/         # the first working app
  tests/             # basic unit tests (node --test)
  docs/
    Phase-1-MVP-Spec.md

License

MIT