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:
- UI framework: React (status quo) vs alternatives
- 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
- Closest migration from React — JSX stays. Components rewrite as:
useState→createSignal,useEffect→createEffect,useMemo→createMemo. Same mental model. - Fine-grained reactivity — components run once, only the DOM nodes that read a changed signal update. Editor latency on every keystroke gets dramatically better.
- Bundle ~7KB vs React's ~40KB — 30KB saved is non-trivial.
- Transparent runtime — you can read the code and predict what runs. No compile-time magic. Matches "obvious when broken."
- CRDT bindings exist —
solid-yjs,@solid-primitives/yjs. Less mature thany-reactbut 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. |
Recommended data architecture
- 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:
- Typst WASM — ~25MB lazy load, compile time per preview. Framework change won't help.
- AI SDK + provider packages — ~150KB in client bundle today. Lazy-load on demand instead.
- Large tables — virtualization already exists for the CV editor; dashboard is fine. Solid would help if no virtualization.
Decisions you're making
- Framework: React (no rewrite) / Solid (recommended) / Svelte 5 (close-second) / Other
- Data layer: LWW (status quo) / Yjs (recommended) / Loro / Hybrid
- Timing: Combined with the existing rehost (Astro → Vite) or separate?
- Scope: All editable docs at once, or pilot on
cv_profilefirst?
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 stronglyCLAUDE.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+ explicitmigrateStackboundary: 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)