Skip to content

Introducing Kerf.

The smallest cut. ~11 KB. No virtual DOM. No compiler. No magic. Reactive UI that touches only the bytes that changed.
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.

Keyed list operations — median ms, lower is better (krausest js-framework-benchmark · 2026-05-20)
Framework swap rowsremove rowclear 1kpartial updateselect 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.

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.

Read the full pillars →

  • 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. mount per island; delegate survives 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 →

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, kerformanceperformance jokes were written. They were also rejected.)

If kerf saves you time on a project you ship, sponsoring on GitHub keeps it actively maintained. Any amount is appreciated.