React DevTools Profiler Guide
Use the React DevTools Profiler to find and fix unnecessary re-renders, understand flame graphs, and apply React.memo / useMemo / useCallback where they actually matter.
Profiler Workflow
- Install React DevTools extension (Chrome Web Store or Firefox Add-ons)
- Open DevTools → React tab → Profiler sub-tab
- Enable "Record why each component rendered" in Profiler settings (gear icon)
- Click Record, perform the slow interaction, click Stop
- Examine the flame graph — look for wide yellow bars (slow components)
- Click a bar to see why it rendered (prop/state change details)
Reading the Flame Graph
| Bar Color | Meaning | Action |
|---|---|---|
| Yellow | Slowest relative to other renders | Investigate — memoize or optimize |
| Green | Fast render | No action needed |
| Grey | Did not render (skipped) | Desired — memoization worked |
| Blue | Mounted for first time | Expected on initial render |
React.memo — Prevent Parent-Triggered Re-renders
// WITHOUT memo: re-renders every time Parent re-renders
function ExpensiveChart({ data, title }: Props) {
return <svg>{/* complex chart rendering */}</svg>;
}
// WITH memo: only re-renders when data or title change (shallow compare)
const ExpensiveChart = React.memo(function ExpensiveChart({ data, title }: Props) {
return <svg>{/* complex chart rendering */}</svg>;
});
// Custom comparator for deep comparison or selective prop ignoring
const ExpensiveChart = React.memo(
function ExpensiveChart({ data, title, onHover }: Props) {
return <svg>{/* ... */}</svg>;
},
(prev, next) => prev.data === next.data && prev.title === next.title
// ↑ ignores onHover changes — only re-render when data or title changes
); useCallback — Stable Function References
// PROBLEM: new arrow function on every render breaks React.memo
function Parent() {
const [count, setCount] = useState(0);
// BAD: new reference every render → MemoizedChild always re-renders
return <MemoizedChild onClick={() => setCount(c => c + 1)} />;
}
// SOLUTION: stable reference with useCallback
function Parent() {
const [count, setCount] = useState(0);
// GOOD: same function reference unless deps change
const handleClick = useCallback(() => {
setCount(c => c + 1);
}, []); // empty deps = never recreated
return <MemoizedChild onClick={handleClick} />;
} useMemo — Expensive Calculations
// BAD: recalculates on every render
function UserList({ users, filter }: Props) {
// This runs even when unrelated state changes
const filtered = users.filter(u => u.role === filter);
return filtered.map(u => <UserRow key={u.id} user={u} />);
}
// GOOD: only recalculates when users or filter changes
function UserList({ users, filter }: Props) {
const filtered = useMemo(
() => users.filter(u => u.role === filter),
[users, filter] // recalculate only when these change
);
return filtered.map(u => <UserRow key={u.id} user={u} />);
}
// Rule of thumb: use useMemo only when:
// 1. The calculation takes > 1ms (profile first!)
// 2. The result is passed to a React.memo component as a prop
// 3. The result is used as a dependency of useEffect/useCallback Programmatic Profiling with the Profiler Component
import { Profiler, ProfilerOnRenderCallback } from 'react';
const onRender: ProfilerOnRenderCallback = (
id, // component name
phase, // 'mount' or 'update'
actualDuration, // time for this render
baseDuration, // estimated time without memoization
startTime,
commitTime
) => {
if (actualDuration > 16) { // flag renders that miss 60fps
console.warn(`Slow render in ${id}: ${actualDuration.toFixed(2)}ms`);
// Send to monitoring: analytics.track('slow_render', { id, actualDuration });
}
};
// Wrap your slow subtree
<Profiler id="DashboardChart" onRender={onRender}>
<DashboardChart data={chartData} />
</Profiler> Common Anti-Patterns and Fixes
- Object literals as props —
style={{ color: 'red' }}creates new object each render; extract to constant or useMemo - Context re-rendering all consumers — Split large contexts; use separate providers for state that changes at different rates
- Array index as key — Use stable IDs; index keys break reconciliation when list order changes
- Not virtualizing long lists — Use react-window or @tanstack/react-virtual for lists over 50-100 items
- useState in hot paths — Each setState causes a re-render; batch updates with useReducer or flushSync
- Premature memoization — Memoization adds overhead; only apply where Profiler shows actual slowness
Frequently Asked Questions
How do I open the React DevTools Profiler?▼
Install the React DevTools browser extension (Chrome or Firefox). Open DevTools (F12 or Cmd+Opt+I) and look for the React tab, which has two sub-tabs: Components (inspect the component tree) and Profiler (record and analyze renders). The Profiler tab shows a Record button — click it, perform the interaction you want to profile, then stop recording. React DevTools only works in development mode (process.env.NODE_ENV === 'development').
What does a Profiler flame graph show?▼
The flame graph shows the component render tree for a single commit (React re-render cycle). Each bar represents a component — width indicates render duration, color indicates time relative to other renders (yellow = slow, green = fast, grey = did not render). Click any bar to see why that component rendered: it shows which props or state changed. Bars that are grey (did not render) mean React skipped them — this is the desired outcome after memoization.
When should I use React.memo, useMemo, and useCallback?▼
React.memo wraps a component to prevent re-render when parent re-renders and props are unchanged — use it for expensive components that receive stable props. useCallback memoizes a function reference — use it when passing callbacks to React.memo'd child components to prevent prop equality failures. useMemo memoizes a computed value — use it for expensive calculations that depend on specific inputs. Do NOT apply these reflexively; measure with the Profiler first, then add memoization only where renders are measurably slow.
Why does React re-render a memoized component even though I added React.memo?▼
React.memo does a shallow comparison of props. Re-renders still happen if: (1) a prop is an object or array literal created inline — a new reference is created on every parent render; (2) a callback prop is an arrow function without useCallback — same issue; (3) a context value changes and the component is a consumer; (4) the component calls useState or useReducer and the local state changes. The fix is to ensure all object and function props are stable references via useMemo/useCallback, or refactor to avoid passing unstable props.
What is the useProfiler hook and when should I use it?▼
React's Profiler component (not the DevTools) is a built-in React component that lets you measure render performance programmatically. Wrap any subtree with <Profiler id='name' onRender={callback}> and the onRender callback is called with render duration, phase (mount vs update), and component path. Use it to collect performance data in staging builds that you send to monitoring services. Unlike DevTools, it works in production builds but should be conditional to avoid overhead in hot paths.
What are React's most common performance anti-patterns?▼
The most impactful anti-patterns: (1) Placing expensive components inside render functions of frequently-updating parents — extract them or lift state down; (2) Putting large state objects in context — context re-renders all consumers on any change; (3) Using array index as key in lists with dynamic order — keys should be stable IDs; (4) Not virtualizing long lists — use react-window or react-virtual for lists over 100 items; (5) Missing dependency arrays in useEffect/useMemo/useCallback — causes re-runs on every render.
Related Products
on GumroadDevPlaybook Interview Vault
210+ real technical interview questions with model answers, asked at Google, Meta, Amazon, Microsoft, Stripe, and Shopify. Covers Frontend (55), Backend (55), System Design (50), and DSA (50). Available in English and Traditional Chinese.
Buy on Gumroad →TypeScript Quick Reference Toolkit
5 practical reference files for TypeScript developers: cheatsheet, TS vs JS comparison, 20 utility types with real examples, tsconfig best practices template, and 15 common mistakes guide. Print it. Keep it on your desk.
Buy on Gumroad →Developer Productivity Bundle
Stop rebuilding the same setup every project. 51 VSCode snippets, 40 Git aliases, 5 Docker Compose stacks, 5 GitHub Actions workflows, 5 README templates, and a dotfiles kit. Set up a professional dev environment in under an hour.
Buy on Gumroad →You Might Also Need
Web Performance Checklist
Interactive web performance checklist covering images, fonts, JavaScript, CSS, caching, and server. Check off items to track your score out of 100 and export as Markdown.
Open free tool →
React Native StyleSheet Generator
Generate React Native StyleSheet.create() code from CSS-like input. Convert web CSS to React Native styles with proper camelCase properties and number values.
Open free tool →
JS Prototype Chain Visualizer
Explore JavaScript prototype chains interactively. Visualize built-in types (Array, Map, Promise, Error) and custom classes with property lookup path diagrams.
Open free tool →
More Free Tools
Related Articles
Performance Optimization 2026: Complete Guide to Frontend, Backend, and Infrastructure
Comprehensive performance optimization guide for 2026 covering Core Web Vitals, JavaScript bundle optimization, server-side rendering, caching strategies, database query optimization, CDN configuration, and real-world benchmarks.
React Performance Optimization in 2026: Techniques for Fast Applications
Master React performance optimization with modern techniques including memoization, code splitting, virtualization, concurrent features, and profiling strategies.
React Performance: useMemo vs useCallback vs React.memo — When to Use Each
Clear explanation of React's useMemo, useCallback, and React.memo with real performance benchmarks. Learn when these optimizations help, when they hurt, and how to measure the difference.
React Hooks Deep Dive: useEffect, useMemo, useCallback Performance Guide
Complete guide to React hooks performance in 2025. Master useEffect lifecycle patterns, useMemo vs useCallback differences, and when each hook helps or hurts performance.
React Hooks in 2026: Advanced Patterns and Best Practices
Master React Hooks in 2026 with advanced patterns for state management, side effects, performance optimization, and building custom hooks that scale.
Enjoying this tool? Get more like it.
Weekly developer tool guides, productivity playbooks & AI tricks. Free. No spam.
Unlock AI-Powered Dev Tools
- ⚡ AI Code Review, Doc Generator & SQL Builder
- ⚡ All premium templates & early access
- ⚡ Member discounts on Gumroad products
$9/mo after trial · Cancel anytime
Want the full toolkit?
Get DevPlaybook Pro
Every template, guide, boilerplate, and automation script in one bundle. 13 premium products — grab them all at once and save big.
MIT licensed · Instant download · No subscription
See what's included → Browse all deals →