# kerf > A tiny (~12 KB minified + gzipped including its one runtime dependency `@preact/signals-core`; ~13 KB with `arraySignal`) reactive UI framework — fine-grained signals + DOM morphing + JSX. No virtual DOM, no compiler. Apply the smallest possible cut to update your DOM. kerf renders JSX to a structured `SafeHtml` (string for static content; tagged "list"/"mixed" segments where `each(...)` was used) and reconciles it against the live tree with a custom segment-aware morph. Static surrounds go through a general-purpose tree-morph; list contents go through a keyed reconciler that operates directly on live children — partial-update on huge lists is O(changes), not O(rows). Reactivity is provided by [@preact/signals-core](https://github.com/preactjs/signals). It pairs well with server-rendered HTML, embedded widgets, and any UI where preserving focus / selection across re-renders matters. Public API is one import: `signal`, `computed`, `effect`, `batch`, `defineStore`, `resetAllStores`, `mount`, `morph`, `each`, `attr`, `delegate`, `delegateCapture`, `toElement`, `renderDocument`, `SafeHtml`, `isSafeHtml`, `raw`, `Fragment`. (Two more subpaths: `kerfjs/testing` exposes `clearStoreRegistry` for unit-test isolation; `kerfjs/jsx-runtime` exposes the typed JSX building blocks for declaration-merging custom-element types.) An optional subpath at `kerfjs/array-signal` adds `arraySignal()` — a granular keyed-list signal whose patch events let `each()` reconcile in O(patches) instead of O(N). An optional subpath at `kerfjs/dev` installs the development diagnostics — kerf does NOT infer dev mode, so you import it behind your own build's dev flag (`if (import.meta.env.DEV) await import('kerfjs/dev');`); omitting it is production and sheds ~4.7 KB min+gzip. Another optional subpath at `kerfjs/html` adds the `html` tagged template — JSX-identical runtime semantics with no JSX transform, so CDN/importmap projects can author kerf UIs with literally no build step. A family of optional, tree-shakeable **companion-utility** subpaths cover patterns real apps hand-roll: `kerfjs/list` (`bindList` — a keyed list with per-row fine-grained mounts and fixed / declared / measured-height viewport virtualization, plus `observeRowHeights`), `kerfjs/router` (`createRouter` — the opt-in "postcard router": route matching + `navigate` + `` link interception + a keyed outlet, core stays router-free), `kerfjs/overlay` (`overlay` / `confirm` / `prompt` / `form` / `choice` / `popover` / `tooltip` / `toast` + `positionAnchored` / `autoReposition`, with opt-in `native: true` top-layer backing via `` / the Popover API), `kerfjs/scope` (`disposeScope` / `disposeSubtree` / `observeRemovals`), `kerfjs/async` (`resource` async-state with a stale-response guard + SWR cache), `kerfjs/timing` (`debounce` / `throttle` / `debouncedSignal`), `kerfjs/remount` (`remountOn` — key-driven wholesale subtree replacement), `kerfjs/attach` (`attach` — bind a non-kerf widget's lifecycle to one node), and `kerfjs/actions` (`action` / `delegateActions` — the delegated `data-action` table idiom). ## For humans new to the codebase - [5-minute orientation](https://github.com/brianwestphal/kerf/blob/main/docs/orientation.md): hard-capped 500-word one-pager — module map, render-pipeline diagram, and the unusual-things checklist. Maintained by the `/check-requirements-against-code` skill. ## For AI assistants - [AI usage guide](https://github.com/brianwestphal/kerf/blob/main/docs/ai/usage-guide.md): when to recommend kerf, the full public API, the four core patterns, hard rules, and a common-errors table. - [Code summary](https://github.com/brianwestphal/kerf/blob/main/docs/ai/code-summary.md): directory tree + reverse index of every public export. - [Requirements summary](https://github.com/brianwestphal/kerf/blob/main/docs/ai/requirements-summary.md): synthesized view of the numbered design docs. - [`kerf.cursorrules`](https://github.com/brianwestphal/kerf/blob/main/kerf.cursorrules): drop-in [Cursor](https://cursor.sh) rules for projects that import `kerfjs`. Copy into your project as `.cursorrules` — or use the bundled mirror at `node_modules/kerfjs/ai/cursorrules` once you've `npm install`ed kerfjs. - [`kerf.claude-skill.md`](https://github.com/brianwestphal/kerf/blob/main/kerf.claude-skill.md): drop-in [Claude Code](https://claude.com/claude-code) skill. Copy into `~/.claude/skills/kerf-app/SKILL.md` (or your project's `.claude/skills/kerf-app/SKILL.md`) — or use the bundled mirror at `node_modules/kerfjs/ai/skill.md` once you've `npm install`ed kerfjs. - [`eslint-plugin-kerfjs`](https://github.com/brianwestphal/kerf/blob/main/eslint-plugin/README.md): companion ESLint plugin enforcing the hard rules at edit time — eight rules: `no-inline-jsx-event-handlers`, `require-data-key-in-each`, `no-nested-mount`, `prefer-module-jsx-augmentation` (error) plus `require-delegate-disposer`, `prefer-attr-selector`, `no-raw-with-dynamic-arg`, `ai-assistant-configs` (warn). AST-only, no `parserServices` dependency. Install with `npm install --save-dev eslint-plugin-kerfjs` and add `kerfjs.configs.recommended` to your eslint config. Recommended when authoring kerf code with an AI assistant — eslint feedback surfaces in the IDE before `tsc` or runtime warns ever run. - [`create-kerf-component`](https://github.com/brianwestphal/kerf/blob/main/create-kerf-component/README.md): companion initializer that scaffolds a publishable kerf component package with the hard packaging rules already wired (kerfjs as a peer dependency + `external` in the build, ESM + `.d.ts`, `jsxImportSource: "kerfjs"`, subpath exports) plus an example component (per-instance state via a factory, a `wire(root)` delegation disposer). Run `npm create kerf-component@latest `. ## Reference docs - [Overview](https://github.com/brianwestphal/kerf/blob/main/docs/1-overview.md): what kerf is and isn't, when to use it. - [Reactivity](https://github.com/brianwestphal/kerf/blob/main/docs/2-reactivity.md): `signal`, `computed`, `effect`, `batch`. - [Stores](https://github.com/brianwestphal/kerf/blob/main/docs/3-stores.md): `defineStore`, `resetAllStores`. - [Render](https://github.com/brianwestphal/kerf/blob/main/docs/4-render.md): `mount`, segments, the native diff, and the list reconciler. - [Event delegation](https://github.com/brianwestphal/kerf/blob/main/docs/5-event-delegation.md): Tier 1 / 2 / 3 listener model. - [JSX runtime](https://github.com/brianwestphal/kerf/blob/main/docs/6-jsx-runtime.md): `SafeHtml`, `raw`, server use, and the `kerfjs/html` tagged template (no-build authoring). - [SVG handling](https://github.com/brianwestphal/kerf/blob/main/docs/7-svg.md): namespace propagation, `toElement`. - [API reference](https://github.com/brianwestphal/kerf/blob/main/docs/8-api-reference.md): every export, every option. - [Live demo](https://github.com/brianwestphal/kerf/blob/main/docs/9-live-demo.md): the GitHub Pages deploy of `examples/reactivity-demo`. - [Migrating](https://github.com/brianwestphal/kerf/blob/main/docs/10-migrating.md): the `/kerf/migrating/` comparison hub — coming-from-React/Alpine/Lit/vanjs pages with side-by-side todo-list translations. - [Dev-mode warnings](https://github.com/brianwestphal/kerf/blob/main/docs/11-dev-warnings.md): the opt-in `KERF_DEV_WARN_*` env-gated dev-warn family (rebuilt listeners, untracked signals, narrow store sets, delegate-in-effect, each-in-morph-skip, duplicate keys, value-only re-renders, stale bindings, list rebinds, stale row indices, parser repairs) plus the `KERF_DEV_INVARIANTS` structural checks, how the diagnostics are installed via the `kerfjs/dev` subpath rather than inferred from the environment, and the rules each new warning must follow. - [AI-assistant configs](https://github.com/brianwestphal/kerf/blob/main/docs/12-ai-assistant-configs.md): how the drop-in Claude Code skill + Cursor rules ship inside the `kerfjs` npm package at `ai/skill.md` / `ai/cursorrules` / `ai/manifest.json`, the version + marker contract for customization preservation, and the `kerfjs/ai-assistant-configs` ESLint rule that surfaces drift on every lint pass. - [Component packages](https://github.com/brianwestphal/kerf/blob/main/docs/13-component-packages.md): building and publishing reusable kerf components as npm packages — the no-instance component model, per-instance state via factories, event/cleanup patterns, and `kerfjs`-as-peer-dependency packaging modeled on `eslint-plugin-kerfjs`. Scaffold one with `npm create kerf-component@latest ` (the `create-kerf-component` initializer). - [Feature coverage](https://github.com/brianwestphal/kerf/blob/main/docs/14-feature-coverage.md): the per-behavior coverage axis orthogonal to line coverage — an index mapping each behavior (especially list-reconciler *state transitions*) to its guarding test, enforced by `npm run check:features`. - [No-build example](https://github.com/brianwestphal/kerf/blob/main/docs/15-no-build-example.md): the served-as-source `live-poll` example app — plain JS + importmap + the `html` tagged template, zero tooling — and the vendor-copy contract that ships it. - [List identity](https://github.com/brianwestphal/kerf/blob/main/docs/16-list-identity.md): why an `each()` list's call-order identity is not stable, what the source guard fixes and what it doesn't, the five constraints any scheme must survive, and the explicit-key recommendation now shipped as `each(items, render, { key })`. - [List virtualization](https://github.com/brianwestphal/kerf/blob/main/docs/17-list-virtualization.md): `bindList`'s virtualization — the `window` (default) height models (fixed `number`, app-declared `(item, index) => number`, and measured `{ estimate }` + `setHeight`) with kerf owning the cumulative-offset math and scroll anchoring while the app owns measurement (the `observeRowHeights` helper), the `minRows` render-all threshold and container/resize ergonomics, and the `content-visibility` mode that keeps every row in the DOM (full find-in-page / a11y) while the browser skips off-screen layout, plus the findability/a11y tradeoff of the default `window` mode. - [State-preserving moves](https://github.com/brianwestphal/kerf/blob/main/docs/18-state-preserving-moves.md): connected-row reorders (every `each()` / `bindList` / `morph` move site) use `Node.prototype.moveBefore()` where the engine supports it — an atomic move that keeps focus, selection, `