Key Takeaways
- Aggressive code splitting reduced initial bundle by 60%
- Hybrid state management (React Query + Zustand) eliminated unnecessary re-renders
- Virtualized lists are essential for 1000+ item datasets
- Measure with React DevTools Profiler before optimizing
- Production performance ≠ development performance
Introduction
Scaling a React application to handle millions of users is no small feat. After working on a project that grew from a small startup MVP to serving over 1 million daily active users, I've learned some valuable lessons that I want to share.
Why This Matters
Most React tutorials focus on getting started. But the real engineering challenge begins when your app hits scale, slow renders, bloated bundles, and state management nightmares. This post covers battle-tested patterns from production.
Code Splitting: The Foundation
One of the first things we implemented was aggressive code splitting. React's lazy loading combined with Suspense made this surprisingly easy.
const Dashboard = lazy(() => import('./Dashboard'));
const Settings = lazy(() => import('./Settings'));
const Analytics = lazy(() => import('./Analytics'));
function App() {
return (
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
<Route path="/analytics" element={<Analytics />} />
</Routes>
</Suspense>
);
}
This reduced our initial bundle size by 60%, significantly improving first contentful paint.
Memoization Strategies
Not everything needs to be memoized, but knowing when to use useMemo, useCallback, and React.memo is crucial.
When to Memoize
- Expensive calculations: Any computation that takes more than a few milliseconds
- Reference equality: When passing objects or functions to optimized child components
- Stable references: For dependencies in other hooks
When NOT to Memoize
- Simple primitive values
- Components that always re-render anyway
- When the cost of memoization exceeds the benefit
State Management at Scale
We started with Redux but eventually migrated to a hybrid approach:
- Server state: React Query for all API data
- UI state: Local component state with Context for shared UI concerns
- Global state: Zustand for truly global application state
This separation made our codebase much more maintainable and reduced unnecessary re-renders.
Virtual Lists for Large Data
When dealing with lists of thousands of items, virtualization is essential. We used react-virtual to render only visible items:
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 50,
});
Key Takeaways
- Measure first: Use React DevTools Profiler before optimizing
- Start with architecture: Good component structure prevents many performance issues
- Lazy load aggressively: Users shouldn't download what they don't need
- Cache smartly: React Query's caching alone solved 80% of our data-fetching performance issues
- Monitor in production: Performance in development doesn't reflect production reality
Building for scale is a journey, not a destination. These patterns have served us well, but the React ecosystem continues to evolve with features like Server Components and Concurrent Rendering opening new possibilities.
💡 Strategic Insight
This isn't just technical knowledge — it's the kind of engineering thinking that separates production systems from toy projects. Apply these patterns to reduce costs, improve reliability, and ship faster.
Frequently Asked Questions
Only after measuring with React DevTools Profiler. Premature optimization wastes time, focus on architecture first, then optimize measurable bottlenecks.
Redux works but a hybrid approach (React Query for server state, Zustand for global UI state) often results in less boilerplate and better performance.
Code splitting with React.lazy(), it's low effort but typically reduces initial load by 40-60%.
Tagged with
TL;DR
- Aggressive code splitting reduced initial bundle by 60%
- Hybrid state management (React Query + Zustand) eliminated unnecessary re-renders
- Virtualized lists are essential for 1000+ item datasets
- Measure with React DevTools Profiler before optimizing
- Production performance ≠ development performance
Need help implementing this?
I help teams architect scalable systems, build AI-powered applications, and ship production-ready software.

Written by
Gaurav Garg
Full Stack & AI Developer · Building scalable systems
I write engineering breakdowns of major tech events, architecture deep dives, and practical guides based on real production experience. Every post is built from code, not theory.
7+
Articles
5+
Yrs Exp.
500+
Readers
Get tech breakdowns before everyone else
Engineering insights on AI, cloud, and modern architecture — delivered when it matters. No spam.
Join 500+ engineers. Unsubscribe anytime.



