Keeping a React App Fast as It Grows
A React app that felt instant at launch can feel sluggish a year later without a single dramatic change. It is a hundred small additions: another chart library, a date library imported whole, a...

A React app that felt instant at launch can feel sluggish a year later without a single dramatic change. It is a hundred small additions: another chart library, a date library imported whole, a context that re-renders half the tree. The fixes are not clever, but they need doing on purpose.
Measure before you touch anything
Two numbers matter. Load: run Lighthouse on a throttled connection and a mid-range device profile, and look at the largest contentful paint and the total blocking time. Interaction: use the framework profiler while you click around, and look for renders that are frequent and expensive. Fix the one the numbers point at, not the one you assume.
Keep the bundle honest
Every dependency is weight the user downloads. Check what is in the bundle with an analyser. The usual offenders: a utility library imported as a whole when you use two functions, a date library that ships every locale, a component kit where you use one component. Import only what you need, pick lighter alternatives, and move anything large and rarely used behind a dynamic import so it loads when the route that needs it does.
Split by route
The user on the login page does not need the code for the admin dashboard. Route-level code splitting is mostly free in modern React setups and it is the single biggest lever on first load for an app with many screens.
Fix the renders that actually hurt
Do not scatter memoisation around hoping. Profile, find the component that renders far more often than it should and does real work each time, and address that one. Often the cause is a value or callback created fresh on every parent render and passed down as a prop, or a context whose value object is rebuilt every time. Stabilise those and the extra renders stop.
Watch the lists
Long lists are a common source of jank. Render only what is on screen with virtualisation, give each row a stable key, and keep the per-row component light. A list of a thousand rows that renders all thousand will stutter on scroll no matter how fast the rest of the app is.
Make it a habit, not a rescue
Put a bundle size check and a Lighthouse run in the pipeline so a regression shows up in the pull request that caused it, while it is one change and easy to trace. Performance kept up gradually is cheap. Performance recovered in a crisis is not.
Common questions
Is a big bundle or slow rendering the more common problem?
Bundle size hits first load and shows up on slower devices and connections. Render performance hits interaction. Measure to see which one is your problem before fixing either.
Does adding memo everywhere help?
No. Memoisation has a cost and most components do not need it. Profile, find the component that re-renders too often and is expensive, and memoise that one.
What tools should we use?
A Lighthouse run on a throttled connection for load, and the framework profiler for renders. Both are free and built in.


