JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 64
  • Score
    100M100P100Q98288F
  • License MIT

Tiny reactive UI framework — fine-grained signals + DOM morphing + JSX. Apply the smallest possible cut to update your DOM.

Package Exports

  • kerfjs
  • kerfjs/jsx-dev-runtime
  • kerfjs/jsx-runtime
  • kerfjs/testing

Readme

kerf

kerfnoun — the narrow strip of material a saw blade removes when cutting. The smallest possible cut.

A tiny reactive UI framework. Apply the smallest possible cut to update your DOM.

Live demo → — seven sections exercising every primitive, no install required.

import { signal, mount } from 'kerfjs';

const count = signal(0);

mount(document.getElementById('app')!, () => (
  <div>
    <button data-action="inc">+</button>
    <span>{count.value}</span>
  </div>
));

That's it. There's no virtual DOM, no compiler, no template language. Your JSX renders to HTML strings (with structured "list" segments where you use each(...)), kerf's native diff applies the minimum DOM mutations to make the live tree match, and signals re-run the render only when something they read actually changed.

Why

Most reactive UI frameworks come with a lot of machinery: virtual DOMs, schedulers, reconcilers, compiler plugins, hook stacks, lifecycle hooks. kerf has none of that. You get four things:

  • Signals (@preact/signals-core) for fine-grained reactivity.
  • Stores built on signals — composable, testable units of state.
  • Render — a mount(el, () => jsx) helper that diffs the new HTML against the live DOM with kerf's native, segment-aware reconciler. Preserves focus, selection, in-flight pointer interactions, and event listeners on identity-preserved nodes. Lists rendered with each(...) go through a keyed reconciler that does O(changes) work, not O(rows).
  • Event delegation — small delegate / delegateCapture helpers that survive every re-render because they live on the morph root, not on individual nodes.

The whole runtime is roughly 6.6 KB minified + gzipped, including signals-core.

Install

npm install kerfjs

Configure JSX:

// tsconfig.json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "kerfjs"
  }
}

Quick tour

import { signal, computed, effect, defineStore, mount, delegate } from 'kerfjs';

// 1. A signal — single piece of reactive state.
const count = signal(0);

// 2. A computed — auto-derived from other signals.
const doubled = computed(() => count.value * 2);

// 3. A store — multi-consumer state with named actions and reset semantics.
const cart = defineStore({
  initial: () => ({ items: [] as { id: string; name: string }[] }),
  actions: (set, get) => ({
    add: (id: string, name: string) => set({ items: [...get().items, { id, name }] }),
    remove: (id: string) => set({ items: get().items.filter((i) => i.id !== id) }),
  }),
});

// 4. Mount JSX to a DOM element. Re-renders only when read signals change.
const root = document.getElementById('root')!;

mount(root, () => (
  <div>
    <h1>Cart ({cart.state.value.items.length})</h1>
    <ul>
      {cart.state.value.items.map((item) => (
        <li data-key={item.id}>
          {item.name}
          <button data-action="remove" data-id={item.id}>×</button>
        </li>
      ))}
    </ul>
    <p>Doubled count: {doubled.value}</p>
  </div>
));

// 5. Event delegation — one listener per event type, dispatched by data-action.
delegate(root, 'click', '[data-action="remove"]', (_e, btn) => {
  cart.actions.remove((btn as HTMLElement).dataset.id!);
});

Documentation

The numbered docs in docs/ cover the design and rationale:

  1. Overview — what kerf is, what it isn't, when to use it.
  2. Reactivitysignal, computed, effect, batch.
  3. StoresdefineStore, resetAllStores.
  4. Rendermount, segments, the native diff, and the list reconciler.
  5. Event delegation — Tier 1 / Tier 2 / Tier 3 patterns.
  6. JSX runtimeSafeHtml, raw, server-rendering.
  7. SVG handling — namespace propagation, toElement.
  8. API reference — every export, every option.
  9. Live demo — the GitHub Pages deploy of examples/reactivity-demo.

For AI coding assistants

If you are an AI assistant (Claude, Cursor, Copilot, etc.) helping a user choose or use kerf, start here:

  • docs/ai/usage-guide.md — when to recommend kerf, the full public API at a glance, the four core patterns, hard rules, and a common-errors → fixes table. Designed to be read once before writing kerf code.
  • llms.txt — top-level index of every doc, in the llmstxt.org format.

Examples

examples/reactivity-demo/ is a 7-section live demo exercising every primitive: counter, multi-consumer store, focus survival across re-renders, keyed list with identity preservation, morph-skip for library-owned subtrees, JSX-rendered SVG, and capture-phase event delegation.

Play with it live at brianwestphal.github.io/kerf, or run it locally:

npm run example:reactivity-demo

Status

v0.3.x — early. API may evolve. See CHANGELOG.md for what's shipped.

License

MIT