Mastering the React DevTools Profiler to Find Real Performance Bottlenecks
The React DevTools Profiler reveals exactly which components are slow and why. Learn to read flame graphs, spot unnecessary re-renders, and prioritize what to fix.
Why the Profiler Beats Guesswork
Most performance optimization starts with wrong assumptions. Developers add useMemo everywhere, split components randomly, and wonder why nothing got faster. The React DevTools Profiler shows you exactly what's slow, in what order, and why — so you can fix the right things.
Setting Up for Accurate Profiling
Profile in development mode or a production-like build with source maps. Pure production builds are faster but harder to read:
# Profile with a production build but readable names
# vite.config.ts
export default {
build: {
minify: true,
sourcemap: true,
rollupOptions: {
output: {
// Preserve function names for profiler
preserveModules: false
}
}
}
};
In the React DevTools, go to the Profiler tab and enable "Record why each component rendered" in settings — this is the most important setting.
Reading the Flame Graph
The flame graph shows render time per component. Each bar's width represents render duration. Yellow bars are slow; gray bars are fast or didn't render.
Key things to look for:
- Wide yellow bars — this component takes a long time to render
- Many small bars — excessive re-renders even if each is fast
- Deep stacks — a parent re-render cascading through many children
Spotting Unnecessary Re-renders
Click a component in the profiler to see why it rendered. Common culprits:
// PROBLEM: new object created on every parent render
function Parent() {
// This creates a new object reference every render!
const config = { theme: 'dark', size: 'lg' };
return <Child config={config} />;
}
// FIX: memoize the object
import { useMemo } from 'react';
function Parent() {
const config = useMemo(() => ({ theme: 'dark', size: 'lg' }), []);
return <Child config={config} />;
}
// OR: move the constant outside the component
const CONFIG = { theme: 'dark', size: 'lg' };
function Parent() {
return <Child config={CONFIG} />;
}
Using the Ranked Chart
Switch to the "Ranked" chart view to see the slowest components sorted by render time. This is the best starting point — fix the top 3 slowest components and you'll capture 80% of the gains.
Identifying Cascade Re-renders
When context changes cause widespread re-renders, the profiler shows all affected components highlight simultaneously. The fix is to split your context:
// PROBLEM: any state change re-renders all consumers
const AppContext = createContext();
function AppProvider({ children }) {
const [user, setUser] = useState(null);
const [theme, setTheme] = useState('dark');
const [cart, setCart] = useState([]);
return (
<AppContext.Provider value={{ user, theme, cart, setUser, setTheme, setCart }}>
{children}
</AppContext.Provider>
);
}
// FIX: separate contexts by domain
const UserContext = createContext();
const ThemeContext = createContext();
const CartContext = createContext();
// Now cart updates don't re-render user/theme consumers
Profiling in Production with Custom Marks
Add performance.mark() calls to measure specific user flows:
// Wrap key user interactions
function handleSearch(query) {
performance.mark('search-start');
const results = expensiveSearch(query);
performance.mark('search-end');
performance.measure('search', 'search-start', 'search-end');
return results;
}
Workflow: Profiler-Driven Optimization
- Record a user interaction that feels slow
- Find the slowest component in Ranked view
- Check why it rendered — props change, state change, context?
- Fix the root cause (not just add memo)
- Re-profile to confirm improvement
- Repeat with the next bottleneck
The profiler turns performance from an art into a science. Stop guessing — start measuring.
Admin
Cal.com
Open source scheduling — tự host booking system, thay thế Calendly. Free & privacy-first.
Bình luận (0)
Đăng nhập để bình luận
Chưa có bình luận nào. Hãy là người đầu tiên!