Read the docs
Introducing Kerf.
Where to go next
Section titled “Where to go next”Browse examples
API reference
Why Kerf
It works like this
Section titled “It works like this”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. Your JSX renders to HTML strings, 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.
How fast
Section titled “How fast”| Framework | swap rows | remove row | clear 1k | partial update | select row |
|---|---|---|---|---|---|
| kerf 0.6.0 | 23.3 | 16.9 | 19.0 | 27.8 | 7.2 |
| Solid 1.9.3 | 21.4 | 17.3 | 18.9 | 20.2 | 6.0 |
| Vue 3.6.0-alpha.2 | 21.3 | 19.3 | 19.2 | 23.9 | 6.3 |
| React 19.2.0 | 147.4 | 18.1 | 25.1 | 24.2 | 7.7 |
Bold = fastest in column. Full table + every scenario: bench/results.md.
Numbers from the krausest js-framework-benchmark, run locally on a clean machine — see bench/results.md for the full table and methodology. Kerf sits in the same cluster as Vue, vanjs, and Lit on most operations. Solid wins the compiler-driven select row and partial update benchmarks; kerf’s general-purpose runtime trades that gap for an ~11 KB bundle, no compiler step, and a smaller public API.
What kerf is good at
Section titled “What kerf is good at”Small bundle
~11 KB minified + gzipped including signals. The one runtime dependency is @preact/signals-core. No virtual DOM, no scheduler, no concurrent-mode machinery to ship.
No virtual DOM, no compiler
JSX renders to HTML strings; a small reconciler patches the live DOM in place. DevTools shows the real DOM because it is the DOM. Standard tsconfig.json + Vite / esbuild / tsup — no plugin chain.
Focus, selection, listeners survive re-renders
The reconciler morphs instead of rebuilding. Caret position, selection range, and delegated listeners survive every re-render.
Small public API
~16 exports total. No hooks, no lifecycle, no per-instance state. Components are plain functions returning JSX.
Plain TS, plain JSX, plain ESM
Drops into anything that already builds JSX. No custom file extensions, no DSL, no required compiler plugin.
Reach for Kerf when…
Section titled “Reach for Kerf when…”- Hybrid desktop apps (Tauri / Electron) — small bundle, predictable diff, debuggable runtime.
- Embedded widgets — chat bubbles, comment boxes, dashboards dropped into someone else’s page.
- Server-rendered apps with islands — Rails / Phoenix / Django / Hono / Astro.
mountper island;delegatesurvives turbo-frame swaps. - Admin panels & internal tools — reactivity without a 200 KB framework + state lib + router.
- Replacing jQuery — incremental migration; same delegation mental model, modern primitives.
- Prototyping — entire mental model on a postcard.
Full use-cases grid → · When to reach for something else →
Why “kerf”?
Section titled “Why “kerf”?”A kerf is the narrow strip a saw blade removes when cutting — the smallest possible cut. The framework’s job is the same: apply the smallest possible mutation to update your DOM.
(And yes, kerformance → performance jokes were written. They were also rejected.)
Sponsor
Section titled “Sponsor”If kerf saves you time on a project you ship, sponsoring on GitHub keeps it actively maintained. Any amount is appreciated.