Framework / data-layer decision — handoff for fresh chat

Context for a new conversation. Written 2026-04-25 mid-session.

What you're deciding

CareerVector is going through a major rewrite (Astro → Vite SPA + Workers, plus likely a CRDT-based data layer). At the same time, the question is open whether to stay on React or switch to a faster framework. Two coupled decisions:

  1. UI framework: React (status quo) vs alternatives
  2. Data layer: LWW + JSON blobs (status quo) vs CRDT

Constraints stated

  • "Performance is one of the fields where we must get better" — perf is a primary axis
  • "Simple, easy to spot when things go wrong" — DX + transparency
  • "Do it once, solve forever" — no migrations, no bandaids
  • Multi-device, multi-agent collaboration is the actual use case (not just single-user)
  • Has been hitting real sync issues (BUG-2 multi-tab sync, dormant DO state loss, etc.)
  • Wants FOSS, reliable, no vendor lock-in

Framework options (ranked by perf, with DX trade-offs)

Framework Bundle Perf DX Migration cost from React Verdict for CV
Vanilla JS + signals ~0KB Ceiling Bad Total rewrite, write your own primitives No — too much infra work
Rust → WASM (Leptos/Yew/Dioxus) ~300KB+ WASM Theoretical peak, often slower than Solid in practice Rust knowledge required Total rewrite No — DX cost too high, real perf wins not as expected
Lit (web components) ~5KB Very fast Standards-based but quirky (slots, encapsulation) Total rewrite No — DX worse than Solid
Qwik ~10KB Best first-paint via "resumability" Unusual mental model ("everything serializable") Total rewrite Niche — matters if landing-page perf is critical
Svelte 5 ~5KB Top tier (signals via runes) Compact templates, slight compile-magic to debug Full component rewrite Strong perf, but compiler magic vs your "obvious when broken" priority
SolidJS ~7KB Top tier (signals from day one) JSX (React-like), explicit signals Components port relatively directly (JSX stays, useState → signals) Top recommendation
Vue 3 ~30KB Faster than React, slower than Solid/Svelte Strong defaults, batteries-included Full component rewrite Sideways move — not worth it
React (status quo) ~40KB Baseline Very mature None Baseline — works but slowest of the options

Why Solid wins for CareerVector

  1. Closest migration from React — JSX stays. Components rewrite as: useStatecreateSignal, useEffectcreateEffect, useMemocreateMemo. Same mental model.
  2. Fine-grained reactivity — components run once, only the DOM nodes that read a changed signal update. Editor latency on every keystroke gets dramatically better.
  3. Bundle ~7KB vs React's ~40KB — 30KB saved is non-trivial.
  4. Transparent runtime — you can read the code and predict what runs. No compile-time magic. Matches "obvious when broken."
  5. CRDT bindings existsolid-yjs, @solid-primitives/yjs. Less mature than y-react but functional.

Why Svelte 5 is close-second

Svelte 5 added runes (signals) so it's now reactivity-equivalent to Solid. Trade-offs:

    • Slightly smaller bundle
    • More compact template syntax for simple components
    • Bigger rewrite from React (templates not JSX)
    • More compile-time transformation makes some bugs harder to trace
    • CRDT bindings less mature (y-svelte exists but smaller community)

Data layer options

Option What Fit Verdict
Yjs Most mature CRDT, JS-native, huge ecosystem Editable documents (CV/CL profiles, jobs, settings) Recommended for editable docs. Fits Cloudflare DO + Workers naturally. Has React/Solid bindings. Apache 2.0.
Loro Newer Rust-based CRDT, ~10× faster than Yjs Same shape as Yjs Worth piloting. Smaller ecosystem than Yjs (fewer bindings, smaller community). Bet against time.
Automerge Multi-platform CRDT (Rust core + bindings to Swift/Kotlin/etc.) Same as Yjs but with native mobile in mind Choose if native (non-PWA) mobile is a real goal. JS perf slightly worse than Yjs.
TinyBase Tabular CRDT, ~6KB Flat data: jobs dashboard, settings Great for dashboard, awkward for nested CV docs. Possible HYBRID: TinyBase for jobs + Yjs/Loro for CV.
ElectricSQL SQLite + Postgres sync Whole stack Wrong fit — pulls you off Cloudflare.
Roll your own Custom ops log on D1 Total control Skip — reinventing a CRDT poorly is the typical outcome.
LWW + field merge (status quo) What you have today Single-author or rare-collision use Doesn't match the "multi-device + multi-agent collaboration" goal.
  • Editable documents (cv_profile, cl_profile, jobs, settings) → Yjs (or Loro if betting on perf)
  • Append-only logs (telemetry, AI usage) → existing pattern, no change
  • Read-only outputs (Typst PDFs in R2, snapshots) → no change
  • Realtime worker rewrites from "broadcast relay" to "Yjs broker" (canonical Y.Doc per workspace, persists ops to DO SQLite, snapshots to D1)
  • REST endpoints become read-only views + action triggers; mutations go through Yjs ops

Benchmarks for verification

  • js-framework-benchmark (Krausest) — the canonical reference. Solid and Svelte 5 trade lead positions. React 10-25% behind. Vanilla JS upper bound.
  • Worth running locally with CareerVector-shaped patterns (editor with N items, dashboard with N rows) before final commit.

Real perf bottlenecks (framework is NOT the only one)

Switching framework helps editor-keystroke latency. But other heavy hitters:

  1. Typst WASM — ~25MB lazy load, compile time per preview. Framework change won't help.
  2. AI SDK + provider packages — ~150KB in client bundle today. Lazy-load on demand instead.
  3. Large tables — virtualization already exists for the CV editor; dashboard is fine. Solid would help if no virtualization.

Decisions you're making

  1. Framework: React (no rewrite) / Solid (recommended) / Svelte 5 (close-second) / Other
  2. Data layer: LWW (status quo) / Yjs (recommended) / Loro / Hybrid
  3. Timing: Combined with the existing rehost (Astro → Vite) or separate?
  4. Scope: All editable docs at once, or pilot on cv_profile first?

My recommendation in one paragraph

Solid + Yjs, executed in one combined rewrite alongside the Astro → Vite rehost. Solid because it's the highest-perf option that doesn't break your React-style mental model and migrations are mostly mechanical. Yjs because it's the only data layer that genuinely solves the multi-device + multi-agent collaboration goal you've stated. Combine with the rehost because the realtime worker has to be rewritten either way. Pilot on cv_profile (the most-edited document); if the experience is meaningfully better, roll out to other documents. Estimated effort: 6-10 weeks of focused work.

If you disagree on any axis, the place to push is: do you actually need multi-collaborator concurrent editing today? If not, the LWW path is cheaper and a framework switch alone gets you most of the perf wins without the data-layer surgery.

Files / context for new chat

  • ~/.agent/topics/shared/careervector-vision.md (if exists) — canonical product vision
  • .agent/runs/sync-direction.md — earlier (more conservative) analysis of CRDT, written before the user clarified the multi-device + multi-agent goal more strongly
  • CLAUDE.md — full architectural decision record for the current implementation
  • .agent/runs/ux-exploration.md — Sonnet's audit of current UX issues (recent)
  • .agent/runs/e2e-suite-status.md — test suite health snapshot

What's already in flight (DON'T re-do)

  • Section-level variantGroup state machine: shipped (commits 8dd3c1c, 9ae213f, 403c21f, etc.)
  • Item-level state machine: shipped (commit 9ae213f)
  • Strict normalizeStack + explicit migrateStack boundary: shipped
  • 887/887 unit tests + behavior test scaffold (37 stubs, ~14 implemented as real tests)
  • Production data deep-cleaned of variantGroup accidents
  • .agent/runs/sync-direction.md (earlier sync analysis — read it for context)
Source: wiki/content/archive/2026-05/agent-runs/framework-decision.md