Migrating to Kerf
Kerf doesn’t try to replace your framework. But if you’ve already decided to leave one — or you’re building something new and weighing the cluster — these pages show your current code translated, line for line, into kerf.
The reference app is a classic todo list: add, toggle, delete, filter. Roughly 150 lines in every framework. Big enough to exercise state, keyed lists, delegated events, and persistence. Small enough that you can read the whole thing. (A few pages — htmx, Redux, Astro — use a conceptual mapping instead of a literal TodoMVC because the source isn’t directly comparable.)
Not ready for a full rewrite? You don’t need one. kerf coexists with React/Vue/Svelte on the same page — see Adopting kerf incrementally to migrate one island at a time.
Pick your starting point
Section titled “Pick your starting point”At a glance — the comparison matrix
Section titled “At a glance — the comparison matrix”How kerf compares to each source framework on the dimensions developers usually decide on.
| Dimension | React 19 | Vue 3 | Svelte 5 | Solid | Preact | Alpine 3 | Lit 3 | vanjs 1.5 | kerf |
|---|---|---|---|---|---|---|---|---|---|
| Bundle (min+gz, runtime only) | ~45 KB | ~22 KB | varies (compiled) | ~4.5 KB | ~6 KB (w/ signals) | ~14 KB | ~6 KB | ~1.6 KB | ~11 KB |
| Reactivity model | hooks + virtual DOM | refs + proxies | runes (compiled) | signals + compiler | hooks + virtual DOM | per-component x-data proxies | reactive properties + lit-html | signals + direct DOM | signals + DOM morph |
| Component model | function / class components | composition + SFC | runes inside .svelte | function components | function components | HTML directives | web components | hyperscript factories | plain functions returning JSX |
| Templating | JSX → virtual DOM | <template> DSL | .svelte DSL | JSX (compiled) | JSX → virtual DOM | HTML attributes | tagged-template literals | hyperscript / DOM nodes | JSX → HTML strings → morph |
| Compiler required | no (but @vitejs/plugin-react) | yes (@vue/compiler-sfc) | yes | yes (babel-plugin-jsx-dom-expressions) | no (but plugin) | no | no | no | no |
| Keyed list reconciliation | key prop, virtual-DOM diff | v-for :key | {#each ... (id)} | <For each> | key prop | manual via x-for :key | repeat() directive | manual via signal arrays | each(items, render, key) |
| Focus / selection survival across re-render | manual via refs | manual | usually automatic | usually automatic | manual via refs | manual | manual | manual | automatic in morph() |
| Event handling | per-node JSX handlers | @click= attributes | onclick={fn} on JSX | per-node JSX handlers | per-node JSX handlers | @click= attributes | @click= template directives | hyperscript handlers | delegate() once on the root |
| Server rendering | first-class | first-class | first-class | first-class | first-class | none | declarative shadow DOM | vanX.replace story | SafeHtml.toString() |
| State sharing across components | Context / Redux / Zustand | provide/inject + Pinia | Svelte stores | createStore | Context / signals | Alpine.store | global state libs | shared van.state() | defineStore |
(Alpine, htmx, Angular, jQuery, Redux, and Astro aren’t directly comparable on every row — see those specific pages for the conceptual mapping.)
Perf snapshot
Section titled “Perf snapshot”Cross-framework perf comparisons are only published from official benchmark runs — clean machine, no background load, results re-generated under controlled conditions. The current committed numbers live at bench/results.md. Headline cluster positions (lower is better on the krausest js-framework-benchmark):
create 1k/replace 1k/append 1k— kerf, Vue, Lit, vanjs, Preact, React are all within ~20% of each other; Solid leads.partial update— Solid and Vue lead; Lit, Preact, React are tightly clustered; kerf and vanjs sit in the next band.select row— Solid, Vue, React lead a tight cluster; kerf sits at the top of the band with Preact and Lit.swap rows— kerf, Solid, vanjs, Vue, Lit lead a tight cluster; React is an outlier (much slower).remove row/clear 1k— within typical noise across the cluster.
The deciding factor between most of these frameworks is the architecture / bundle / tooling tradeoff covered in each per-framework page, not row-update latency. Solid and Svelte 5 are the exceptions where compiler-driven update-path performance is a real, measurable lead — if that’s your decision driver, those are the right answers.
How the per-framework pages are structured
Section titled “How the per-framework pages are structured”Each page (with adjustments for non-TodoMVC sources) follows the same five-section shape:
- Bundle delta — what the swap costs (or saves) in bytes.
- Mental-model translations — a table mapping the source framework’s primitives to kerf’s.
- Side-by-side code — the same todo list, side by side, section by section (state, render, events, list).
- Gotchas — what trips developers coming from this specific framework.
- Perf numbers — qualitative or quoting
bench/results.mdwhere the framework is in the comparison set.
Not your starting point?
Section titled “Not your starting point?”If your framework isn’t listed, the React page is the closest fit for any hooks-shaped framework, the vanjs page is closest for “no template language, direct DOM,” and the jQuery page is closest if you’re modernizing from a non-reactive imperative codebase.
If your app is built around heavy component composition (<DataGrid>, <DatePicker>, deep prop drilling), kerf probably isn’t your next stop — see When to reach for something else.