After building React applications for 9+ years across companies like India Today Group, Clove Dental, and multiple startups, I've compiled the performance techniques that actually matter in production.
1. Memoization Done Right
Most developers misuse React.memo() and useMemo(). The key is to only memoize components that receive referentially unstable props and render expensive trees.
// ❌ Useless memoization
const Button = React.memo(({ onClick }) => <button onClick={onClick}>Click</button>);
// ✅ Useful — prevents expensive list re-renders
const DataTable = React.memo(({ rows, columns, onSort }) => {
// Renders 1000+ rows with complex cells
return <table>...</table>;
});
2. Code Splitting with Dynamic Imports
At India Today, our editorial dashboard has 40+ features. Loading everything upfront would be insane. We use route-based and component-based splitting:
// Route-based splitting in Next.js
const ElectionDashboard = dynamic(() => import('./ElectionDashboard'), {
loading: () => <DashboardSkeleton />,
ssr: false, // client-only for chart libraries
});
3. Virtual Scrolling for Large Lists
When rendering election results with 500+ constituencies, virtual scrolling is essential. We use react-window to render only visible rows:
import { FixedSizeList } from 'react-window';
<FixedSizeList height={600} itemCount={constituencies.length} itemSize={72}>
{({ index, style }) => <ConstituencyRow data={constituencies[index]} style={style} />}
</FixedSizeList>
4. Image Optimization with Next.js
We reduced LCP by 40% by switching to next/image with proper sizing, priority loading for above-fold images, and blur placeholders.
5. Debouncing & Throttling API Calls
Search autocomplete in our CMS fires on every keystroke. Without debouncing, that's 10+ API calls per second:
const debouncedSearch = useMemo(
() => debounce((query) => fetchResults(query), 300),
[]
);
6. State Management Architecture
We moved from Redux to a hybrid approach — React Context for UI state, React Query/SWR for server state. This eliminated 60% of our Redux boilerplate.
7. Bundle Analysis & Tree Shaking
Run npx next build --analyze regularly. We found that importing lodash instead of lodash/debounce added 70KB to our bundle.
8. Lazy Loading Below-the-Fold
Using Intersection Observer to load components only when they scroll into view — exactly what this portfolio uses for scroll animations.
9. Web Workers for Heavy Computation
Election night data processing (parsing 10,000+ results in real-time) runs in a Web Worker to keep the UI thread responsive.
10. Profiling with React DevTools
The React Profiler is your best friend. We profile every release candidate and flag any component that re-renders more than 3 times per user interaction.
Results
These optimizations helped us achieve: LCP under 1.2s, TTI under 2s, and 95+ Lighthouse performance score on India Today's editorial platform serving millions of daily users.