What are the limitations of React when building large scale applications?
React is a view library, not a framework — no built-in routing, data fetching, forms, or state management. The team picks N libraries; mismatches and version drift accumulate. Out-of-box bundle is small but unguided architecture leads to bloat. No conventions for folder structure, code splitting, SSR — different shops solve it differently. Performance is up to the developer. Hooks rules + closures are easy to get wrong. RSC is still maturing.
React is a library, not a framework. That's its strength and its limitation at scale.
1. Not opinionated
React gives you:
- A component model.
- Hooks.
- Reconciliation.
You provide:
- Routing.
- Data fetching.
- Forms.
- State management.
- Build pipeline.
- Folder structure.
- SSR strategy.
- Code splitting.
Two teams using React can produce wildly different codebases — that's the limitation.
2. Performance is your problem
The framework doesn't prevent bad patterns:
- Re-renders cascade unless you memoize.
- No built-in selector pattern for Context.
- No code splitting unless you opt in.
- No image optimization (frameworks like Next.js add this).
A 'plain React' app of moderate size is often slower than a Next.js or Remix app because the team didn't invest in the missing pieces.
3. Async + closure pitfalls
- Stale closures in useEffect.
- Setting state after unmount.
- Missing deps in dependency arrays.
- Effects that fight each other.
These are real bugs that linger in big codebases. React 19's compiler helps but doesn't solve.
4. State management is everyone's problem
Server state, client state, URL state, form state, ephemeral UI state — all conflated unless the team draws boundaries. The result: Redux storing server state, Context updating per-keystroke, useEffect syncing both. The 'state management problem' in React is mostly self-inflicted by mismodeling.
5. Forms
No built-in form story. Teams choose Formik (slow on large forms), React Hook Form (good but yet another lib), or roll their own. Validation is separate (Yup, Zod). For large apps with many forms, this fragmentation costs.
6. SSR is not built in
renderToString and hydrateRoot exist but the wiring (routing, data, streaming, caching) requires a framework. Hand-rolled SSR is hard. Next.js / Remix / Astro have become the standard answer.
7. RSC is still maturing
React Server Components solve real problems (zero-JS server-rendered content, streaming, data co-location). But:
- Only Next.js App Router has a complete implementation.
- The mental model ('use server' / 'use client') trips up teams.
- Tooling (DevTools, error messages) lags the model.
8. Hooks rules are easy to break
- Don't call conditionally.
- Don't call in loops.
- Don't suppress exhaustive-deps without understanding.
ESLint catches most but the trap is real for new contributors.
9. Bundle size grows silently
React itself is 45 KB. But teams pull in: moment (300 KB), lodash (full import: 80 KB), Sentry, analytics, design system, charts — and the initial bundle hits 1 MB without anyone noticing. Frameworks with bundle budgets enforce; bare React doesn't.
10. Testing has multiple stories
Jest vs Vitest. React Testing Library vs Enzyme (legacy). MSW vs fetch mocks. Different shops, different choices. Onboarding to a new React codebase often means learning their specific testing setup.
Where React shines despite this
- Composability: components compose well.
- TypeScript: integration is excellent.
- Ecosystem: every library you might need exists.
- Hiring: largest talent pool.
- Stability: React's API hasn't broken hard since hooks (2019).
Mitigations
- Adopt a framework (Next.js, Remix). The 'plain React' approach has limits at scale.
- Pick a stack and stick to it: server state (React Query), routing (Next.js), forms (Hook Form + Zod), state (Zustand).
- Enforce module boundaries (eslint-plugin-boundaries, CODEOWNERS).
- Bundle budgets in CI.
- Design system as a separate package.
Senior framing
React's biggest limitation is also its biggest strength — it's small. That means every team makes architectural decisions. With discipline (frameworks + conventions + linting) it scales fine. Without, it collapses into div-soup, re-render storms, and a Frankenstein of half-adopted libraries.
Follow-up questions
- •Why do most production React apps now adopt a framework?
- •What's the biggest scaling problem you've seen in plain React?
- •How do React Server Components address the SSR limitation?
Common mistakes
- •Treating plain React as a complete framework — leads to ad-hoc architecture.
- •Choosing too many state libraries — Redux + Zustand + Context + useState all in one app.
- •Skipping module boundaries — features import each other's internals.
Performance considerations
- •Plain React's performance is what the team makes of it. Frameworks bring opinionated defaults (image optimization, route splitting, font subsetting) that prevent regressions. Without those, the team has to build them.
Edge cases
- •Teams that try to recreate Angular DI in React with deeply nested Context.
- •Codebases that ship 'works in dev, breaks in production' due to ad-hoc SSR.
- •Polyglot teams (React + Angular + Vue) sharing components — usually a sign architecture decisions were skipped.
Real-world examples
- •Successful large React apps almost always use a framework: Vercel (Next.js), Linear (custom), Notion (heavily customized), Shopify (Hydrogen on Remix). 'Plain React + create-react-app' is rare at scale in 2025.