React everywhere is not a law of physics. It was a smart default for a decade of web UX, but it doesn’t mean your SaaS should be a single, perpetually hydrating app in 2026. The latest wave of teams ripping out React in favor of htmx and server‑rendered MPAs is not nostalgia; it’s a sober accounting of complexity and cost. An HN post making the rounds details a company removing React and replacing it with htmx for interactivity. That’s a shot across the bow for anyone paying on-call costs for hydration bugs without shipping commensurate UX.
What’s changed since we all bet on SPA?
- The browser got fast, your app got CPU‑bound. Modern engines and HTTP/2+ make multipage navigations cheap. Your real bottleneck is JS parsing, hydration, and main‑thread thrash.
- Most B2B surface area is still CRUD. If 70–80% of your screens are forms, tables, and pagination, a 14 KB library that swaps HTML fragments is enough. React + react‑dom + router + data layer typically lands 150–300 KB gzipped before your code.
- Core Web Vitals monetized performance. You feel it in SEO and activation funnels. Cutting hydration is the fastest INP/LCP win left for many teams.
- Server frameworks leveled up. Rails 7 + Hotwire, Django + htmx, Phoenix LiveView, Laravel Livewire, Astro Islands—this is not 2013. MPA + selective islands is a first‑class track.
React is great—until it isn’t
React remains an excellent choice when your product is a state‑heavy, collaborative canvas (think Figma, Notion’s block editor, deeply interactive dashboards) or requires offline‑first guarantees. But for data entry workflows, admin panels, marketing/user lifecycle flows, knowledge bases, settings pages, you are paying a permanent platform tax for a problem you don’t have.
If you suspect you’re in that camp, stop arguing taste and run the numbers.
A CTO’s decision framework: Should you remove React?
Score your product on five axes (0–5 each)
- Interactivity density. How many concurrent stateful controls per view? 0 = simple forms/tables, 5 = nested draggable components, editors, synchronized widgets. Threshold: If average screens ≤ 2, score ≤ 2.
- Real‑time collaboration. Multi‑cursor, presence, conflict resolution. 0 = none, 5 = core to product. Threshold: Any ≥ 3 keeps you in SPA land for that surface.
- Offline/Background sync. Does it need to work with flaky connectivity? 0 = no, 5 = yes, with robust local state. Offline ≥ 3 argues for SPA or native.
- Navigation model. Do views stand alone with clean URLs, or is it one big scene? 0 = MPA‑friendly, 5 = SPA‑centric with shared scene state.
- Regulatory/SEO pressure. Crawlability, render determinism, and privacy (no client PII in JS). 0 = low, 5 = high. High values favor SSR/MPA.
Heuristic: If your weighted average is ≤ 2.5, you will likely ship faster, crash less, and score better with an MPA + progressive enhancement stack. If ≥ 3.5, keep React where it adds real leverage. If you’re in the middle, mix: an MPA shell with a few React or Web Component islands.
The financial case: What you stop paying for
- Bundle and hydration tax. htmx is ~14 KB gzipped. A typical React stack (react + react‑dom + router + query + your glue) lands 150–300 KB gzipped. Trimming 200 KB and skipping hydration routinely yields 15–30% better LCP and marked INP gains on mid‑range devices.
- State bugs and on‑call time. Hydration mismatches, stale caches, effect loops—if your incident rotations know these by heart, you’re funding a Rube Goldberg machine. Teams we’ve moved off SPA patterns routinely cut UI incident volume by 20–40% in three months.
- Developer throughput drag. Shipping a CRUD flow in React often means writing the same schema three times (server, client, validator) and threading state through query/mutation layers. With SSR + htmx, you render once, progressively enhance where needed, and lean on battle‑tested server validations and transactions.
Architectures that win in 2026
1) MPA + htmx (or Turbo) + sprinkles
Keep your server framework in the driver’s seat. Render HTML; swap fragments via htmx; add small alpine.js or vanilla sprinkles for local UI state. Your mental model is straightforward: the URL maps to a server action that returns HTML. Progressive enhancement handles 80% of interactivity without a full client state runtime.
2) MPA + Islands
Use an islands framework (Astro, Marko, Qwik) to ship 90% HTML and wake discrete interactive components on demand. For complex widgets—data grid with virtualization, WYSIWYG editor—mount a React/Vue/Svelte island in a page otherwise rendered on the server. You keep React where it pays for itself.
3) LiveView‑style SSR over websockets
Phoenix LiveView and similar patterns let the server own state while pushing diffs to the client. This model crushes a wide range of back‑office tools and dashboards with minimal client JS, strong security boundaries, and excellent performance.
Risks and trade‑offs (don’t pretend there aren’t any)
- Hiring pool and momentum. React has the biggest candidate pool and ecosystem. htmx and LiveView are simpler but less common on resumes. Mitigation: upskill a nearshore pod to seed patterns and pair with your core team for 8–12 weeks.
- Component ecosystem. You’ll miss React‑first components. Plan for Web Components, headless UI, or build the few high‑value widgets you need once as islands.
- Client‑side routing expectations. SPA transitions can feel snappier. Use server‑driven preloading, HTTP/2 push alternatives, and partial swaps to get 95% of the feel with 5% of the code.
- Feature flags and A/B infra. If your experimentation tooling is JS‑first, you’ll need server‑side variants and deterministic rendering. Net positive for observability, but some plumbing work upfront.
Migrate without rewriting the company
Full rewrites fail. Strangle the SPA instead.
Phase 0: Instrument the pain
- Measure today. Core Web Vitals by path, JS bytes by route, crash rate, hydration error count, p95 INP on mid‑range Android.
- Tag your surfaces. Label routes as CRUD, marketing, settings, editor, dashboard, etc. You will not migrate them equally.
Phase 1: Prove it on low‑risk surfaces (2–4 weeks)
- Marketing and auth flows. Move login, signup, password reset, email verification to SSR. These benefit from deterministic rendering and SEO anyway.
- Settings and profiles. Replace client state with server forms and htmx fragment swaps. Expect instant bundle wins and fewer edge‑case bugs.
- Measure again. If you don’t see double‑digit LCP/INP improvements and lower UI incident counts on these paths, stop. Your SPA may be justified.
Phase 2: Carve out a server‑rendered shell (4–8 weeks)
- Route by route, move navigation, headers, footers, and most pages to server rendering. Keep your complex widgets mounted as React islands inside those pages. This delivers 80% of the benefits without touching your hardest UI.
- Adopt a fragment API. Standardize endpoints that return partial HTML for lists, pagination, and modal content. These become your new UI primitives.
- Shared validations. Centralize schema and validation on the server. The client does progressive enhancement, not business logic.
Phase 3: Decide the fate of your complex widgets (8–16 weeks)
- Keep React where it pays. Editors, heavy data grids, real‑time collab: leave them as islands. Use lazy hydration and only ship code where needed.
- Simplify what you can. Many “complex” widgets are not core value. A pageable table and modal form can replace bespoke grid+inline‑edit combos without hurting UX.
Governance and performance gates
- Set budgets. Per‑route JS budget (e.g., ≤ 50 KB gz for non‑island pages), p75 INP thresholds, error budgets for hydration mismatches (target: zero on MPA routes).
- Codeowners and ADRs. Any new SPA surface requires an Architecture Decision Record with explicit justification against the five axes. Default is MPA + enhancement.
Security, privacy, and compliance get easier
- Less client PII. Rendering on the server means you can avoid shipping sensitive fields to the browser until absolutely necessary. That shrinks your data exposure surface.
- Fewer cross‑cutting side effects. No sprawling client caches and global stores to leak state across tenants or sessions.
- Deterministic audit. What the server rendered is what the user saw. That matters for regulated flows and legal holds.
Real examples to calibrate your gut
- Basecamp/HEY popularized Hotwire/Turbo to power complex SaaS without a SPA runtime, reporting big performance and complexity wins.
- GitHub has long used PJAX/Turbo‑like techniques with server‑rendered HTML and sprinkles. Nobody calls GitHub slow.
- Django + htmx teams ship admin‑heavy products in weeks, not months, leaning on server forms, transactions, and optimistic UI via fragment swaps.
These are not toy apps. They prove that if your value prop is workflow and data correctness—not a pixel‑perfect multiplayer canvas—server‑first works and scales.
How we run this as a nearshore engagement
If you do not have cycles to fight this fight alone, seed it with a pod that has shipped this pattern repeatedly. Our Brazil‑based teams work in your hours (6–8 hours overlap U.S. time) and start with a two‑sprint proof on low‑risk flows. We codify patterns, build your fragment API, and leave you with budgets, lint rules, and a migration runway. You keep React where it belongs; you stop hydrating everything else.
FAQ for skeptical CTOs
“We invested in a design system built on React. Are we throwing it away?”
No. Treat it as an island library. Export headless primitives as Web Components where it makes sense. Most visual tokens, CSS, and accessibility work transfers cleanly.
“What about complex client caching and optimistic updates?”
Put consistency first: server‑authoritative mutations wrapped in transactions. For perceived speed, stream partials, use HTTP caching aggressively, and apply optimistic UI on the handful of interactions that warrant it. You do not need a global client cache for a settings page.
“Will we lose snappy transitions?”
Use client‑side prefetch on hover, htmx boosted links, and partial swaps. The difference vs SPA is negligible when you are not asking a mid‑range phone to parse 300 KB of JS first.
“Isn’t this just trading one complexity for another?”
Yes—except the new complexity is closer to your data and invariants, where you already have tests, transactions, and observability. That is a better place to spend your limited complexity budget.
A simple rule you can defend to your CEO
If a screen can be described as “load data, render form/table, submit, and show result,” it is guilty until proven innocent of needing a SPA runtime. Use React where collaboration, offline, or heavy client computation make it clearly cheaper. Everywhere else, stop paying the hydration tax.
Key Takeaways
- Default to MPA + progressive enhancement; add React as an island where it pays for itself.
- Score each surface on interactivity density, collaboration, offline needs, navigation model, and SEO/regulatory pressure.
- Expect 15–30% LCP/INP gains and 20–40% fewer UI incidents when you de‑hydrate CRUD surfaces.
- Migrate by strangling the SPA: start with auth, marketing, and settings; then carve a server‑rendered shell; keep complex widgets as islands.
- Set per‑route JS budgets and require ADRs for any new SPA surfaces.
- Security and compliance improve when rendering deterministically on the server with less client PII.
- Use a seasoned nearshore pod to seed patterns, pair with your team, and exit with guardrails in 8–12 weeks.