Wiring It Together
You have all three machines: reactivity that knows when data changes (phases 1-2), a describer that says what the UI should be (phase 3), and a diff that finds the least to do about it (phase 4). A framework is these three in a loop. Today: the loop - and then the satisfying part, mapping your 120 lines onto the four frameworks' vocabularies.
The whole framework, assembled
Read top to bottom - every line is something you built - then run it:
// ══ 1. REACTIVITY (phases 1-2) ══════════════════════════════
const ledger = ; let activeEffect = null;
// ══ 2. DESCRIPTION (phase 3) ════════════════════════════════
// ══ 3. DIFF (phase 4, compact) ══════════════════════════════
// ══ 4. THE LOOP: mount() ties them together ═════════════════
// ══ 5. AN APP - just a component and state ══════════════════
const state = ;
;
// Simulate three "clicks" and a rename - watch the patches:
state. = 1;
state. = 2;
state. = 3; // count hits the limit: TEXT change AND disabled flips - one write, two patches
state. = 'Total';
What just happened, and it's worth savoring: mount wraps the component call in an effect.
Rendering reads s.count and s.title through the proxy - so the render effect subscribes to
exactly the state the UI uses, automatically. Every later write re-runs the effect, which
re-describes the UI, diffs against the previous description, and reports the minimal change. The
third click is the beauty shot: one state write produced two coordinated patches (button text
plus disabled flipping) because the description derived both from the same value.
That's a UI framework. State in, minimal updates out, nothing manual in between.
The map: your 120 lines → their million
Now every framework term from our frontend guides has a line number in your own code:
| You built | React calls it | Vue calls it | Svelte calls it | Angular calls it |
|---|---|---|---|---|
reactive() proxy |
- (state via setters instead) | reactive() / ref |
$state proxies |
signal() |
activeEffect tracking |
- | template/effect tracking | rune auto-tracking | signal graph |
effect() |
useEffect (cousin) |
watchEffect |
$effect |
effect() |
computed() + dirty flag |
useMemo |
computed |
$derived |
computed() |
h() / vnodes |
createElement / JSX |
template → vnodes | - (compiled away) | template → instructions |
renderToString |
server rendering | SSR | SSR | Angular SSR |
diff() |
reconciliation | patching | - (precompiled updates) | change detection |
| keys exercise | key |
:key |
keyed {#each} |
track |
The dashes teach as much as the entries. React has no reactive proxy - it re-runs components wholesale and leans entirely on the diff (which is why it insists on immutable updates: identity is its only change signal). Svelte has no runtime diff - its compiler knows the dependencies at build time, so it ships direct updates (your phase 2 ledger, resolved ahead of time). Vue and Angular sit in between: reactive tracking chooses which components re-render, then templates update efficiently. Four frameworks, one design space - and you've now built enough of it to place any future framework on the map in an afternoon.
Where to take it
Stretch goals, each a real weekend project on this foundation:
- Real DOM. Replace
renderToString+ patch logs withdocument.createElementand actual patch application - the same walk, mutating nodes. (Do it in a scratch HTML file; the logic transfers line for line.) - Effect re-tracking. Fix phase 2's stale-subscription exercise properly: clear an effect's subscriptions before each run.
- Keyed diff in the main algorithm. Merge your phase 4 exercise into
diffso keyed children move instead of rewriting. - Read the giants. Vue's
@vue/reactivitypackage is your phases 1-2 with production hardening - and it's readable now. So is Preact's diff (a famously compact real-world reconciler).
Recap
- A framework is a loop: reactive state → render effect describes UI → diff finds minimal
change.
mountis fifteen lines. - Rendering inside an effect is the keystone: reading state during render is the subscription.
- One write can coordinate many patches because the description derives everything from state.
- The comparison table is yours now - including why React demands immutability and Svelte ships no diff.
- The magic box is empty. It was code all along - about 120 lines of it.
← Phase 4: The Diff · Guide overview