Task Board, Built Four Times
I built the same task board four times — React, Vue, Svelte, and Angular — using Claude Code, to see how the AI-assisted experience actually differs across frameworks. Same API, same features, four very different rides.
- Done: all four apps built and reviewed, "ran easily" / "folder structure" / usage rated for all four, all four deployed to Cloudflare Pages
-
Pending: "understood the code" ratings, the assignee hover-card task (not done for
any app yet) — this page and
docs/COMPARISON.mdwill update as those land
React
Tailwind CSS + shadcn/ui + Lucide React
Open the app →Vue
Tailwind CSS + PrimeVue + PrimeIcons
Open the app →Svelte
Tailwind CSS + Skeleton UI + Lucide Svelte
Open the app →Angular
Angular Material + Material Icons
Open the app →Findings & Comparison
Everything below is pulled straight from my build notes (docs/COMPARISON.md in the repo,
alongside each app's own FEEDBACK.md) — same facts and numbers, just easier to skim than
raw markdown.
Ratings summary
Here's how each one actually went, on a first pass:
| Metric | React | Vue | Svelte | Angular |
|---|---|---|---|---|
| Ran easily | 5/5 | 5/5 | 1/5 | 5/5 |
| Understood the code | pending | pending | pending | pending |
| Folder structure made sense | 4/5 | 4.5/5 | 5/5 | 3.5/5 |
| Weekly usage delta | +2% | +1% | +1% | +1% |
Stack per app
| React | Vue | Svelte | Angular | |
|---|---|---|---|---|
| UI library | shadcn/ui | PrimeVue | Skeleton UI | Angular Material |
| Icons | Lucide React | PrimeIcons | Lucide Svelte | Material Icons |
| Data fetching | TanStack Query | TanStack Query | TanStack Query | HttpClient + RxJS + signals |
Cross-cutting findings
Two things bit me on literally every app, not just one:
The API doesn't send CORS headers, period
First thing I hit: the live API sends no CORS headers at all, so the browser flatly refuses to call
it directly. I confirmed it by throwing an OPTIONS preflight at it and watching it come
back 405 with nothing helpful in the response.
Fixed the same way in every app: proxy /api through the dev server (Vite's
server.proxy for React/Vue/Svelte, proxy.conf.json for Angular), then swap
in a Cloudflare Pages Function to do the same job once it's actually deployed.
The same API returns two different response shapes
columns and categories come back as a plain array. users and
tasks switch to { data, pagination } the moment you pass
limit or offset — same API, different shape depending on which endpoint
and which query params you happen to use.
Every app's API client just normalizes both shapes to a plain array and moves on.
Notable findings
PrimeVue quietly went premium
Building the Vue app, I went to install PrimeVue and found out it's gone premium.
primevue@latest is now 5.0.1, and its own npm description flipped from
"open source" to "a premium UI library" — it also pulls in @primeui/license-manager,
literally described as an "offline license verifier for PrimeUI and PrimeUI PRO." The repo's MIT
LICENSE.md still technically covers it, but I wasn't going to gamble a free portfolio
piece on a dependency that ships its own license-checking code.
Pinned PrimeVue to ^4.5.5 instead — still explicitly "open source," still dist-tagged
v4-stable — with primeicons@^8.0.0 (just a CSS icon font, unaffected
either way). Its theming package, @primevue/themes, turned out to be deprecated too, in
favor of @primeuix/themes — switched to that instead of installing something already
marked dead.
Svelte + TanStack Query v6: this one was rough
Svelte 5's new runes plus a brand-new TanStack Query v6 adapter (rewritten specifically for runes) turned out to be a noticeably shakier combination than anything React or Vue threw at me. First it was just weird — the board would sit there for multiple seconds before anything painted, no error, no explanation. So I did the obvious thing and restarted the dev server. A lot. Which, it turns out, quietly wrecked Codespaces' port-forwarding in the background — and then a second, completely different-looking failure showed up: the board wouldn't load at all through the forwarded browser URL, with nothing in the console or the terminal even hinting at why.
A real chunk of that app's build session went into chasing a bug that had nothing to do with the code. And I couldn't fully fix it myself — I could tell what was probably wrong, but I had no way to reach into Codespaces' forwarding daemon and fix it directly.
The actual fix was removing the stale forwarded port by hand, in VS Code's Ports panel — not something you'd guess. If you hit this cold, with zero error messages pointing anywhere useful, you'd be completely forgiven for just assuming the app was broken and giving up.
Angular 22 wants a newer Node than this repo has
Then Angular. I go to scaffold it with the latest CLI and hit a wall immediately:
@angular/cli@latest is 22.1.6, and it wants Node
^22.22.3 || ^24.15.0 || >=26.0.0. This whole repo's devcontainer is pinned to Node 20 —
shared across all four apps. Angular 22 just isn't going to run in it.
Dropped to Angular ^21.2.22 instead — the newest major still fine with Node 20
(^20.19.0 || ^22.12.0 || >=24.0.0). Same call I made all session with TypeScript (6.0.2
over 7.0.2) and PrimeVue (v4 over v5): take the newest version that doesn't fight what's already
there. I deliberately left the shared devcontainer alone rather than bumping it myself — that's a
bigger change affecting three apps that were already reviewed and signed off, so that call belongs
to me, the project owner, not something to change unilaterally. Not an oversight. A tradeoff.
Deployment
Once everything was built, I actually shipped it — one combined Cloudflare Pages deploy, all four apps and this page under their own paths.
Three clean, one déjà vu
React, Vue, and Angular deployed to Cloudflare Pages and just worked, first try — no surprises. Svelte deployed successfully too, but hit the exact same "stuck on loading columns" flakiness in production that showed up back in dev, needing a few refreshes before it'd actually load.
This is the interesting part: it happened on a completely fresh environment — no Codespaces, no dev server, no port-forwarding layer anywhere in the picture. Which means the original dev-mode slowness probably wasn't just a Codespaces problem after all — there's likely something real going on in the app or query stack itself. Best guess right now: TanStack Svelte Query v6's rune-based query initialization on first mount. Still not confirmed.
Lessons learned
I forgot to spec a .gitignore upfront
node_modules/ and build artifacts got committed before I caught it and cleaned it up after the fact.
Next time: spell out .gitignore requirements in the brief itself, upfront — not patch it in after the first commit.