On this page
Performance Optimization
React is fast by default, but large apps benefit from targeted optimizations.
React.memo
Prevent re-renders when props haven’t changed:
const UserCard = React.memo(function UserCard({ user }) {
console.log('Rendering', user.name);
return <div>{user.name}</div>;
});
// Custom comparison
const UserCard = React.memo(UserCardComponent, (prev, next) => {
return prev.user.id === next.user.id;
});
useMemo and useCallback
function ProductList({ products, category }) {
// Expensive filter — only recompute when deps change
const filtered = useMemo(
() => products.filter(p => p.category === category),
[products, category]
);
const handleSelect = useCallback((id) => {
console.log('Selected:', id);
}, []);
return filtered.map(p => (
<ProductItem key={p.id} product={p} onSelect={handleSelect} />
));
}
Don’t optimize prematurely — measure first.
Code Splitting
Split bundles with lazy and Suspense:
const Dashboard = lazy(() => import('./Dashboard'));
function App() {
return (
<Suspense fallback={<Spinner />}>
<Dashboard />
</Suspense>
);
}
Virtualization for Long Lists
Render only visible items:
npm install @tanstack/react-virtual
import { useVirtualizer } from '@tanstack/react-virtual';
function VirtualList({ items }) {
const parentRef = useRef(null);
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 50
});
return (
<div ref={parentRef} style={{ height: '400px', overflow: 'auto' }}>
<div style={{ height: virtualizer.getTotalSize() }}>
{virtualizer.getVirtualItems().map(vRow => (
<div key={vRow.index} style={{
position: 'absolute',
top: vRow.start,
height: vRow.size
}}>
{items[vRow.index].name}
</div>
))}
</div>
</div>
);
}
Profiling with React DevTools
- Install React Developer Tools browser extension
- Open Profiler tab
- Record interactions and identify slow components
Key Rules
- Keep state close to where it’s used
- Avoid inline objects/functions in props to memoized children
- Use keys correctly in lists
- Split large components into smaller ones
- Defer non-urgent updates with
startTransition(React 18):
import { startTransition } from 'react';
startTransition(() => {
setSearchResults(filtered); // Lower priority update
});
Bundle Analysis
npm run build
npx vite-bundle-visualizer
Identify large dependencies and lazy-load them.
Profile before optimizing — most React apps are fast enough without manual memoization.