React Performance Optimization: Practical Checklist for Production Apps
Performance in React directly affects user experience, Core Web Vitals, and SEO. This article compiles a short, easy-to-apply checklist for incrementally optimizing your React application in production.
React.memo and useMemo
React.memo for Components
const ExpensiveComponent = React.memo(({ data, onUpdate }) => {
const expensiveValue = useMemo(() => {
return data.reduce((acc, item) => acc + item.value, 0);
}, [data]);
return (
<div>
<h3>Total: {expensiveValue}</h3>
<button onClick={onUpdate}>Update</button>
</div>
);
});
useMemo for expensive calculations
function ProductList({ products, searchTerm, sortBy }) {
const filteredAndSortedProducts = useMemo(() => {
const filtered = products.filter(product =>
product.name.toLowerCase().includes(searchTerm.toLowerCase())
);
return filtered.sort((a, b) => {
if (sortBy === 'price') return a.price - b.price;
if (sortBy === 'name') return a.name.localeCompare(b.name);
return 0;
});
}, [products, searchTerm, sortBy]);
return (
<div>
{filteredAndSortedProducts.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
}
useCallback for Event Handlers
function TodoList({ todos, onToggle, onDelete }) {
const handleToggle = useCallback((id) => {
onToggle(id);
}, [onToggle]);
const handleDelete = useCallback((id) => {
onDelete(id);
}, [onDelete]);
return (
<div>
{todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={handleToggle}
onDelete={handleDelete}
/>
))}
</div>
);
}
Code Splitting with React.lazy
Component-level splitting
const LazyDashboard = React.lazy(() => import('./Dashboard'));
const LazyProfile = React.lazy(() => import('./Profile'));
const LazySettings = React.lazy(() => import('./Settings'));
function App() {
return (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/dashboard" element={<LazyDashboard />} />
<Route path="/profile" element={<LazyProfile />} />
<Route path="/settings" element={<LazySettings />} />
</Routes>
</Suspense>
</Router>
);
}
Dynamic imports with conditions
function AdminPanel() {
const [showAdvanced, setShowAdvanced] = useState(false);
const [AdvancedComponent, setAdvancedComponent] = useState(null);
useEffect(() => {
if (showAdvanced && !AdvancedComponent) {
import('./AdvancedSettings').then(module => {
setAdvancedComponent(() => module.default);
});
}
}, [showAdvanced, AdvancedComponent]);
return (
<div>
<button onClick={() => setShowAdvanced(!showAdvanced)}>
Toggle Advanced Settings
</button>
{showAdvanced && AdvancedComponent && <AdvancedComponent />}
</div>
);
}
Virtual Scrolling
For large lists, use virtual scrolling:
import { FixedSizeList as List } from 'react-window';
function VirtualizedList({ items }) {
const Row = ({ index, style }) => (
<div style={style}>
<div className="item">
{items[index].name}
</div>
</div>
);
return (
<List
height={600}
itemCount={items.length}
itemSize={50}
width="100%"
>
{Row}
</List>
);
}
Image Optimization
Lazy loading images
function LazyImage({ src, alt, className }) {
const [isLoaded, setIsLoaded] = useState(false);
const [isInView, setIsInView] = useState(false);
const imgRef = useRef();
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setIsInView(true);
observer.disconnect();
}
},
{ threshold: 0.1 }
);
if (imgRef.current) {
observer.observe(imgRef.current);
}
return () => observer.disconnect();
}, []);
return (
<div ref={imgRef} className={className}>
{isInView && (
<img
src={src}
alt={alt}
onLoad={() => setIsLoaded(true)}
style={{
opacity: isLoaded ? 1 : 0,
transition: 'opacity 0.3s'
}}
/>
)}
</div>
);
}
State Management Optimization
Split state to avoid unnecessary re-renders
// ❌ Avoid - one large state object
function App() {
const [state, setState] = useState({
user: null,
posts: [],
comments: [],
ui: { loading: false, error: null }
});
// Every time any field updates, the entire component re-renders
}
// ✅ Better - split state
function App() {
const [user, setUser] = useState(null);
const [posts, setPosts] = useState([]);
const [comments, setComments] = useState([]);
const [ui, setUi] = useState({ loading: false, error: null });
// Only re-render when relevant state changes
}
Bundle Analysis
Use webpack-bundle-analyzer to analyze bundle:
npm install --save-dev webpack-bundle-analyzer
{
"scripts": {
"analyze": "npm run build && npx webpack-bundle-analyzer build/static/js/*.js"
}
}
Performance Monitoring
Using React DevTools Profiler
import { Profiler } from 'react';
function onRenderCallback(id, phase, actualDuration) {
console.log('Component:', id);
console.log('Phase:', phase);
console.log('Duration:', actualDuration);
}
function App() {
return (
<Profiler id="App" onRender={onRenderCallback}>
<Header />
<Main />
<Footer />
</Profiler>
);
}
Conclusion
Optimizing React performance requires a deep understanding of how React works and applying the right techniques for each situation. These techniques will help your React application run smoothly and provide a good user experience.
Remember: Measure before optimizing. Use React DevTools and browser performance tools to identify real bottlenecks.
Tags: React, Performance, Optimization, JavaScript, Web Development


