Frameworks are easier to leave than either their marketing or their critics suggest, because a React 7,897 codebase is three layers and only one is framework-specific. Components, hooks, context, CSS and their tests are portable by construction — a component that takes props and returns JSX does not know what built it — and that layer is the bulk of most applications. Routing is a day or two of tedium: app/orders/[id]/page.tsx becomes routes/orders.$id.tsx and useParams keeps working. The server edge — data loading, mutations, caching, auth, images — is the real bill.
That is where lock-in lives, and it is worth being precise about it. Next.js 10,514 's lock-in is not Vercel 3,061 : Deployment self-hosts it behind nginx 75 and in Docker 514 , and @opennextjs/aws and @opennextjs/cloudflare run it elsewhere. The lock-in is the programming model. Server Components, "use cache", Server Actions, next/image, next/font, proxy.ts and Route Handlers have no equivalent in React Router 139,386 or TanStack Start 79,069 , and a component that awaits a database query in its body — the thing the App Router encourages most — must be split into a loader and a client component before it can live anywhere else. Astro 17,728 's lock-in is smaller in code and larger in shape: a page of independent islands is not one React tree.
One habit keeps the bill low: keep business logic in plain modules that take arguments and return data, so the framework-specific file only adapts it. A Server Action calling cancelOrder(id) and a React Router action calling the same function are four lines apart.
Practically: start every new project with plain React and Vite 25,978 unless you can name the specific thing a server has to do — index this page, generate this social card, keep this secret, render this before the data arrives. If you can name it, pick the framework whose model matches it and commit to that model rather than fighting it. If you cannot, build the SPA today; the migration you might need later is smaller than the complexity you would carry until then.