The Task Board Experiment

One task board, built four times

Built the same task board four times with the help of Claude Code, to see how easy it is to build a certain front end framework with the help of AI. Same API, same features, four very different outcomes.

Boards are view-only. No drag-and-drop, no create/edit/delete. Just wanted to see how fast I can build the app using different frameworks and tech stack and how they would look like when you give AI the power to decide the implementation.

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 →

Release status

v0.1 — Initial pass

Findings & Comparison

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

Ratings

Metric React Vue Svelte Angular
How easy to launch the app 5/5 5/5 1/5 5/5
How readable the generated code is pending pending pending pending
How easy to add new features manually pending pending pending pending
Folder structure made sense 4/5 4.5/5 5/5 3.5/5
Claude Weekly usage delta +2% +1% +1% +1%

Cross-stack findings

The API doesn't send CORS headers

The live API sends no CORS headers at all, so a browser can't call it directly cross-origin. Confirmed via an OPTIONS preflight — a 405 response with no CORS headers present.

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 return a bare array. users and tasks switch to { data, pagination } once limit or offset is passed — same API, inconsistent shape depending on endpoint and query params.

Every app's API client normalizes both shapes to a plain array.

Notable findings

PrimeVue quietly went premium

Installing PrimeVue for the Vue app surfaced a licensing change: primevue@latest resolves to 5.0.1, whose own npm description now reads "a premium UI library" (previously "open source") and pulls in @primeui/license-manager — described as an "offline license verifier for PrimeUI and PrimeUI PRO" — as a hard dependency. The repo's MIT LICENSE.md still technically covers it, but the license-manager coupling was enough uncertainty to avoid risking it on a free portfolio piece.

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: noticeably less stable

Svelte 5's new runes combined with a brand-new TanStack Query v6 adapter (rewritten specifically for runes) proved noticeably less stable than the React or Vue combinations. Initial dev-mode behavior showed multi-second delays before first paint, with no console or terminal errors. Repeated dev-server restarts while investigating this likely destabilized Codespaces' port-forwarding layer, surfacing a second, unrelated-looking failure: the board wouldn't load via the forwarded browser URL at all, again with nothing in the console or terminal pointing toward a cause.

A significant share of that app's build session went into debugging an issue entirely outside the code. The likely cause was correctly diagnosed, but there was no way to directly access or reset Codespaces' forwarding daemon to confirm or fix it.

The fix was removing the stale forwarded port manually in the Codespaces' Ports panel.

Angular 22 wants a newer Node than this repo has

Scaffolding Angular with the latest CLI hit an immediate wall: @angular/cli@latest is 22.1.6, requiring Node ^22.22.3 || ^24.15.0 || >=26.0.0. This repo's devcontainer is pinned to Node 20, shared across all four apps — Angular 22 won't run in it.

Dropped to Angular ^21.2.22 instead — the newest major still compatible with Node 20 (^20.19.0 || ^22.12.0 || >=24.0.0), consistent with the same tradeoff made throughout the session with TypeScript (6.0.2 over 7.0.2) and PrimeVue (v4 over v5): take the newest version that doesn't conflict with what's already in place. The shared devcontainer was deliberately left untouched rather than bumped — that's a bigger change affecting three apps already reviewed and signed off, so that call was left to the project owner rather than made unilaterally. Not an oversight. A tradeoff.

Deployment

Once all four apps were built, they were combined into a single deployment — one Cloudflare Pages project, 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

Missing .gitignore in the initial brief

node_modules/ and build artifacts were committed before this was caught and cleaned up after the fact.

Lesson: spell out .gitignore requirements in the brief itself, upfront — not patch it in after the first commit.