Vue vs React in My Own Repos: Why React Is My Default, and Where I Still Pick Vue
About twenty of my front ends are React and two are Vue. Why React won for apps that span web and mobile, what it costs me, and the two cases where I still choose Nuxt: a landing page that ships zero JavaScript and a $0 full-stack app on Cloudflare Workers.

I counted the front ends in my GitHub folder. About twenty are React: web consoles, Next.js sites and Expo mobile apps. Two are Vue, and both are recent.
So this post isn't about a weekend where I ported a Vue codebase to React. That never happened. What did happen is less dramatic and more useful. For anything that behaves like an application, React became my default and stayed there. For two specific kinds of project I still choose Nuxt on purpose, and I'd choose it again.
Where React won, and why
When I rebuilt BoltHR's web console in June 2025, the very first commit was "initial commit with shadcn added". There was no framework debate. By then the choice had made itself, for four reasons.
1. One mental model from browser to phone
Nearly every product I build has a mobile app beside the web app: BoltHR's employee app, Opero's handheld scanner, Hoadhaa Estate's tenant and landlord app. They're all Expo and React Native, and they all use TanStack Query, the same hooks pattern, the same axios-plus-interceptor setup and the same TypeScript types as the web clients.
A data hook in the BoltHR web console reads almost exactly like the one in the mobile app. I can work across three codebases in a day without changing how I think. Vue doesn't have a mobile story I'd bet a production app on, and that one gap settles most of my decisions.
2. The admin-console ecosystem
ERPs are mostly forms, tables and side panels. In React I can put one together from parts I already trust: Radix and shadcn/ui for primitives, TanStack Table for grids, React Hook Form with Zod for validation. BoltHR's web app gives every feature the same three files:
features/payroll/
payroll-page.tsx page container
payroll-table.tsx TanStack Table grid
payroll-form-sheet.tsx create/edit form in a side sheet
Opero's Counter, Agent Portal and Platform Admin use the same stack: React 19, Vite, TanStack Query, Zustand. A new screen in any of them is a short copy-and-adapt, not a design exercise. Vue has good equivalents for most of these pieces, but I would be assembling them one by one rather than reusing a toolkit I have already tested in production.
3. TypeScript is just TypeScript
A React component is a function, so props, generics and hook return types flow through plain tsc. Vue 3 is good at TypeScript now, but it still needs its own type checker: the Vue projects I maintain run vue-tsc and nuxt typecheck as a separate step. In a small project I don't notice. In a codebase with forty-odd API hooks, I want the compiler that everything else already uses.
4. Handover
I work in a small team, and several of my projects end up with a client's developers or a contractor. React is the stack people already know. That isn't a technical argument, but it's a real one.
What React costs me
React isn't free, and I'd be lying if I said it was.
- •
useEffectand stale closures still cause the bugs that take the longest to find. Vue's reactivity simply doesn't have that class of bug. - •Too many decisions. Router, data layer, forms, state, styling: React makes you pick all of them. My fix is to pick once and copy the setup everywhere, which is why so many of my apps look alike underneath.
- •Next.js has its own learning curve. The App Router's caching rules and the server/client component boundary are powerful, but for a CRUD admin panel they're often more machinery than the job needs. That's why BoltHR's console and Opero's apps are plain Vite single-page apps, not Next.js.
The two places I still pick Vue
A marketing page that ships no JavaScript
Opero needed a landing page. The first version was one hand-written HTML file. Our decision record turned down Vite and Next.js as "a build, a package and a port for one static page". The same day it was rebuilt as a Nuxt 4 app, so its dozen sections could be real components with shared copy. It is still statically generated with nuxt generate and served by nginx.
An SEO audit later found the page was shipping about 200 KB of Vue runtime for content with no client-side behaviour at all. The FAQ is native . The fix was one line:
export default defineNuxtConfig({
features: { noScripts: true },
})
The home page is now about 8 KB of gzipped HTML plus 4 KB of CSS. I still get components, single-file templates and one copy source feeding the page, the meta tags, the JSON-LD and llms.txt, and visitors download none of the framework. In this case Vue is a very pleasant templating language with a build step, and Nuxt lets me switch off its own runtime. I like that a lot.
A small full-stack app that has to cost nothing
The other Vue project is a "shop and win" promotion app. Staff show a single-use QR code on a counter tablet, customers enter draws from their phones, and the app sends SMS confirmations and runs a live draw reveal. The constraint was a hosting bill of zero.
It runs on Nuxt 4 with Nitro's Cloudflare preset, which builds one Worker plus static assets, with D1 through Drizzle and a cron trigger that drains the notification queue. Pages, API routes and shared validation all live in one project. The Zod schemas that build the dynamic entry form are auto-imported on both client and server, so the form a customer fills in and the check the server runs come from the same definition.
I could build it in Next.js on Workers. But for a small full-stack tool, Nuxt's file-based server routes, auto-imports and Nuxt UI got me from an empty folder to a working product faster than anything I've used in React. Single-file components also make the kiosk screens and their animations easy to read in one place.
What I miss about Vue in the React apps
- •
v-modelon forms. Two-way binding with less code than any React form library I've used. - •Single-file components. Template, logic and scoped styles together, with a clear separation that JSX plus Tailwind never quite matches.
- •Reactivity that does the obvious thing. Fewer memoisation decisions, no dependency arrays to get wrong.
How I decide now
1. Will it have a mobile app that shares logic with the web? React, with Expo. This settles most projects.
2. Is it an admin console of forms and tables? React, Vite, shadcn and TanStack. That's my own stack, already tested in production.
3. Is it content that should ship little or no JavaScript? Nuxt with scripts turned off, or plain HTML if it really is one page.
4. Is it a small full-stack tool on the Cloudflare free tier? Nuxt.
5. Will another team take it over? React, unless they tell me otherwise.
Neither framework is "better". React fits the shape of most of what I build: multi-platform, form-heavy, long-lived products. Vue wins when the project is small, mostly server-rendered, or should feel like plain HTML with good tooling. If I ever build a product with no mobile app and a team that already knows Vue, I'd happily start in Nuxt.