React Isn't Slow. Your Architecture Is.
— and then someone says we should rewrite it in something else, because React “doesn't scale,” before anyone opens the profiler.
Published on November 28, 2025

That sentence shows up in enough meetings that I can finish it from memory. The app feels heavy. Interactions hitch. First paint is fine; everything after feels sticky. The library on the logo becomes the villain because it is easier to blame a brand than a render tree.
React is rarely the bottleneck. Architecture is: what you render, when you hydrate, how data moves, and how much of the page pretends it needs to be interactive on first load.
The usual suspects (none of them are “React”)
Start with hydration. If you ship a large client bundle and ask the browser to hydrate a forest of components that barely need event handlers, you will feel lag that has nothing to do with JSX syntax. The cost is work: parse, evaluate, attach listeners, reconcile. Architecture decides how much of that work is mandatory.
Then rendering strategy. A page that re-renders a giant table because a sidebar toggle flipped state is not a React failure. It is a boundary failure. State lived too high. Props sprayed too wide. Memoization became a superstition instead of a response to a measured hot path.
Data flow is the third. Waterfalls of client fetches after paint. Spinners chained to spinners. Context that updates like a global event bus. The UI looks “React-y” while the network and state graph do the real damage.
- Hydrating interactive chrome that could have stayed static HTML
- Fetching in the leaf instead of shaping data at the edge
- Passing unstable object/array props through deep trees
- Animating layout thrash with JS when CSS would have been enough
- Shipping one mega-bundle because “code splitting later”
Profile before you rewrite
Rewrites are emotionally satisfying. They let the team feel decisive. They also reset institutional knowledge and usually recreate the same architecture mistakes under a new logo.
Profile first. Look at what commits. Look at what blocks the main thread. Look at whether the slow interaction is layout, script, or network. Most “React is slow” stories collapse into a handful of concrete offenders once you stop debating frameworks and start reading a flame chart.
When the chart points at your app, the fix is usually architectural and boring: split the route, move data closer to the server, shrink the client island, stabilize props, stop rendering what the user cannot see. None of that requires abandoning React. It requires respecting the cost model you already chose.
Architecture that stays fast
I do not mean micro-optimizations as a personality. I mean defaults that keep the expensive path rare.
Prefer server rendering for content that is mostly read. Keep client components where interaction earns the cost. Colocate data with the screen that needs it instead of hydrating a god-store for every page. Treat lists as dangerous by default — virtualize or paginate before the table becomes a performance memoir.
Also watch your “global” habits. A theme provider that re-renders the world on every toggle. A user context that updates on every keystroke in a search box. A layout that remounts children because keys were unstable. These are architecture choices that feel like React quirks until you measure them.
And be honest about “feels slow.” Perception is part of the product. A fast paint with a clear loading state often beats a late perfect render. Architecture includes sequencing, not only throughput.
If your plan is “switch frameworks,” and your evidence is vibes, you are about to spend a year relocating the same mess.
React is a tool for describing UI over time. It will happily describe a bad system with high fidelity. When the app feels slow, interrogate hydration, rendering boundaries, and data flow before you put the library on trial.
The logo is not the villain. The architecture you wrapped around it usually is.
A useful team rule: no framework migration discussion until someone has attached a profile, named the top three costs, and proposed a fix that stays inside the current stack. Most of the time the fix is smaller than the debate. Occasionally the architecture really is wrong — and even then, you migrate with a map, not a mood.
Slow apps are usually honest. They are showing you where the boundaries failed. Listen to that before you change the logo on the README.
One more pattern I see constantly: blaming React for a page that downloads half the product to show a settings form. The library did not ask you to hydrate every route. Your routing and bundling choices did. Fix the map of what loads when. Leave the framework debate for a day when the flame chart is boring.
Performance is an architectural conversation wearing a stopwatch. Treat it that way and React stops being a scapegoat. Treat it as a brand war and you will still be slow — just somewhere else.