Start with Measurement
Never optimize blindly. Use React DevTools Profiler, Chrome Performance tab, and Lighthouse to identify actual bottlenecks before writing a single line of optimization code.
The Most Common Performance Killers
1. Unnecessary Re-renders
Every state change in a parent re-renders all children. Use React.memo to skip re-renders when props haven't changed:
const ExpensiveComponent = React.memo(({ data }) => {
// Only re-renders when 'data' actually changes
return <div>{processData(data)}</div>;
});2. Expensive Calculations on Every Render
Use useMemo to cache expensive computations:
const sortedUsers = useMemo(
() => users.sort((a, b) => a.name.localeCompare(b.name)),
[users] // Only re-compute when users changes
);3. Unstable Function References
Functions defined in a component body are recreated on every render. This breaks React.memo for child components and causes unnecessary effect re-runs:
// Bad: new function reference every render
const handleClick = () => submitForm(data);
// Good: stable reference
const handleClick = useCallback(() => submitForm(data), [data]);Code Splitting
Don't ship all your code upfront. Use dynamic imports to split your bundle and only load code when needed:
const HeavyChart = lazy(() => import("./HeavyChart"));
function Dashboard() {
return (
<Suspense fallback={<Skeleton />}>
<HeavyChart data={chartData} />
</Suspense>
);
}Virtualization for Long Lists
Rendering thousands of DOM nodes is slow. Use a virtualization library like react-window to only render visible items.
Image Optimization
Use Next.js's <Image> component or loading="lazy" on images. Serve modern formats (WebP, AVIF) and use appropriate sizes.
Key Takeaways
- Measure first, optimize second
- Avoid premature optimization — it adds complexity
- Most React apps don't need aggressive memoization
- Bundle size and initial load often matter more than render performance
Comments (0)
Sign in to join the conversation.
No comments yet. Be the first to share your thoughts.