That 130-file PR that shipped KeyEcho 1.0 contained a decision I never wrote about: the desktop app moved from Tauri 1 + Vue to Tauri 2 + SolidJS.
The same summer, upweb.dev moved from Nuxt 4 to SolidStart, and keyecho.app (SSR, five languages, Stripe checkout) was built on SolidStart from scratch. Three separate decisions, same answer every time.
Scope first. This post is about defaults for my personal projects. Near the end I'll cover two places where I still use React, one of which runs very well.
The criteria changed
Most of the code in my repos is AI-generated now. My job has shifted from writing code to reviewing it.
The old framework checklist put most of its weight on learning curve and developer experience. Both are worth zero now: the model writes five frameworks fluently and knows the rules of hooks better than most humans do. AI crushed the price of writing code. Two costs didn't move: the runtime bytes every user downloads, and the human hours spent reviewing. Pick your stack by the new prices.
Runtime cost lives in the architecture. No prompt removes it, so it decides the framework. Review bandwidth is finite, and AI throughput will grind down any consistency that is maintained by verbal agreement, so it decides the styling layer. I have seen more than one React codebase a few years into its life: two animation libraries, two carousel components, three styling systems, each introduced for a perfectly good reason on some ticket, and the sum is a mess nobody can clean up. AI did not invent that drift. It made it an order of magnitude faster.
React is no longer my default: size and performance
Let me discard the weak arguments first. Dependency arrays, stale closures, re-render storms: I am not going to relitigate any of it. The model knows the rules of hooks cold, eslint-plugin-react-hooks catches most slips, and React Compiler now handles memoization. The ergonomics debate no longer produces a winner.
What still produces a winner is the client. react-dom is roughly 40 kB compressed, a virtual DOM plus a reconciler, downloaded, parsed, and executed on every cold visit by every user. React Compiler can remove wasted re-renders. It cannot remove the runtime itself. Developers never feel this cost. Users on phones pay it constantly.
My projects are one person plus a fleet of agents. Hiring pools do not help me, and content sites barely touch the component-library ecosystem. Once the weights shift like that, React has no slot left in my personal stack.
The same app, written ten times
Loren Stewart ran an experiment last year: the same kanban app implemented in ten frameworks, identical features (drag and drop, comments, tags, server validation), then compared compressed JS on the board page.
| Framework | Compressed JS |
|---|---|
| Next.js 16 (React 19) | 176.1 kB |
| TanStack Start (React 19) | 118.2 kB |
| Analog (Angular 20) | 103.9 kB |
| Nuxt 4 (Vue 3) | 74.7 kB |
| TanStack Start + Solid | 60.4 kB |
| Qwik City | 58.4 kB |
| SvelteKit (Svelte 5) | 54.1 kB |
| SolidStart (SolidJS 1.9) | 41.5 kB |
| Astro + HTMX | 34.3 kB |
| Marko | 28.8 kB |
Two comparisons stand out. Same meta-framework, TanStack Start, with React swapped for Solid: 118.2 kB becomes 60.4 kB. Next.js against SolidStart: a 4.2x gap. His conclusion was that React's architectural cost is something no meta-framework can optimize away.
The boundaries, as always. All ten implementations had FCP between 35 and 71 ms, indistinguishable by feel. Real-site bottlenecks are usually images and video, and framework JS is a small share of total bytes. But the two costs behave differently: media optimization is ongoing labor for the life of the site, while framework weight is fixed forever at pnpm create. AI already flattened the cost of writing the code. For the same feature set, there is no reason to start four times heavier.
First-party numbers, same site, before and after. On Nuxt 4, upweb.dev scored 70/100/100/100 on mobile PageSpeed. I did round after round of tuning at the Cloudflare layer and the performance score would not move; the lab bottleneck kept pointing at JS weight. After the move to SolidStart: 99/100/100/100, simulated Moto G Power on slow 4G, FCP 1.5 s, LCP 1.8 s, CLS 0.006, TBT 0 ms. That zero TBT is where fine-grained reactivity pays off most: there is no virtual tree to reconcile on the main thread.
Squeeze the water out of this comparison before quoting it: Lighthouse lab data, a single-page session, a sample of one site, and the framework was not the only variable. People will remind you that lab scores are not real user experience, and they are right. RUM tells you how users are doing today; the lab tells you how much headroom the architecture leaves. Same domain, same CDN config, framework swapped, and the score went from 70 to 99. Free points. I took them.
Desktop follows the same logic: inside a Tauri WebView, every kilobyte shaved off the runtime comes back as cold-start time and memory.
I did look at Svelte, Vue, Qwik, and Marko
Svelte 5's runes are signals too, and SvelteKit's 54.1 kB is small enough. Vue's reactivity comes from the same family, and Nuxt's 74.7 kB is the best result among the big-three meta-frameworks. What ruled them out is the language layer.
.svelte and .vue are custom file formats. TypeScript is one region inside the file, and type checking runs through a bridge: svelte-language-tools on one side, Volar on the other. The bridge works fine day to day. The bill arrives when the toolchain moves. TypeScript 7.0 went GA on the 8th of this month, a native Go compiler, officially about 10x faster, with type-checking of the VS Code codebase dropping from 125.7 seconds to 10.6. JSX is plain TS, so a Solid project switches the day it ships, zero changes. The compiler API that Vue and Svelte template checking depends on has no stable version in 7.0. They wait for 7.1 in October. Every extra language layer is another place in the upgrade queue.
One more against Nuxt: streaming SSR is still locked behind the experimental ssrStreaming flag, while streaming in SolidStart is one of createHandler's three built-in rendering modes. No flag to flip.
Qwik's resumability solves huge, interaction-sparse landing pages, which my sites are not, and I did not want to carry its mental model. Marko's 28.8 kB is the smallest of the ten, but the ecosystem is too small a bet for two sites I plan to maintain for years.
Reactivity models have been converging. Vue refs, Svelte 5 runes, Angular and Preact signals, plus the TC39 Signals proposal, all point the same way. React is the lone exception, patching around it with a compiler. Signals, JSX, plain TS: ask for all three at once and Solid is what is left.
Astro is smaller than SolidStart. Why not Astro?
One row in that table deserves a straight answer: Astro + HTMX is 34.3 kB, 7 kB smaller than SolidStart. Both of my sites are content-heavy marketing sites. By byte logic alone, Astro wins.
Astro's model is content first, zero JS by default, interactivity added through islands. The catch is that when you write the interactive parts, each island hosts a component framework anyway, and the project now speaks two dialects: .astro templates for page structure, the island framework for behavior, state passed across the seam. And .astro is one more custom file format, so the upgrade-queue problem from the last section applies here too.
Neither site is actually static. keyecho.app runs Stripe checkout, a subscribe flow, and SSR in five languages, with sound-pack sales pages on the way. upweb.dev has quietly grown into a small system: double-opt-in subscriptions, posts and newsletter archives managed in Broadside, and on every publish a webhook hits the site, verifies its signature, dedupes replays, clears the server content cache, then purges Cloudflare by cache tag. New articles go live without a deploy. All of that lives in SolidStart API routes, and the interaction density only goes up from here. One component model covering both the static and the dynamic parts is worth 7 kB to me.
If your site is a pure blog or docs and changes three times a year, Astro is the better answer, not SolidStart. With a bigger team the best practice is to split outright: landing pages on Astro or SolidStart, and the console on its own subdomain with its own stack.
The ecosystem bill: TanStack covers the worst of it, components you grow yourself
SolidStart is not a silver bullet, and its biggest weakness is the ecosystem. Most things React has, Solid has an equivalent for, one maturity tier down and with fewer options. Part of that you tolerate. Part of it has workarounds.
The workaround with the widest coverage is TanStack. Router, Query, Form, Table, Virtual: every package is a core plus adapters, and Solid gets first-class ones. I run the Solid version of TanStack Start in production at and keyecho.app both run on SolidStart + vanilla-extract today. The next repo I open will probably start there too, until some project's shape changes the answer again.
SOCIAL SHARE CARD GENERATOR