DevTools Performance Analysis
Learn how to record, read, and act on Chrome DevTools performance traces. Master the flame chart, identify long tasks, and connect profiling data to real optimizations that improve FCP, LCP, and responsiveness.
Table of Contents
Overview
Chrome DevTools Performance tab lets you record exactly what the browser is doing — every function call, layout, paint, and network request — and visualize it on a timeline. It's the single most powerful tool for diagnosing why a page feels slow.
The workflow is simple: measure (record a trace), analyze (read the flame chart and identify bottlenecks), and optimize (fix the code, then re-measure to confirm). No guessing, no premature optimization — just data-driven decisions.
Understanding DevTools profiling is essential for interviews (you'll be asked "how would you debug this?") and for real-world work where you need to ship fast, responsive UIs.
The golden rule of performance
Never optimize without measuring first. DevTools tells you exactly where time is spent — so you fix the actual bottleneck, not what you assume is slow.
Why Performance Analysis Matters
Performance isn't abstract — it directly impacts user behavior, business metrics, and search rankings. Here's why profiling skills matter.
User Experience
Users notice delays over 100ms. A laggy click handler or janky scroll makes your app feel broken, even if it's functionally correct.
Core Web Vitals
Google measures FCP, LCP, INP, and CLS. Poor scores hurt search rankings. DevTools is how you diagnose and fix these metrics.
Real-World Debugging
'The dashboard is slow' is a common bug report. Without profiling, you're guessing. With DevTools, you can pinpoint the exact function causing the delay.
Common Scenarios You'll Debug
- →Slow initial page load — large bundles, render-blocking resources
- →Laggy interactions — click handlers taking 200ms+, input delay
- →Janky scrolling — layout thrashing, expensive paint operations
- →Slow React re-renders — unnecessary component updates, large trees
- →Memory leaks — growing heap, detached DOM nodes
Interview context
When asked "How do you debug performance issues?" — start with "I open Chrome DevTools Performance tab, record a trace of the slow interaction, and look at the flame chart to identify long tasks on the main thread." This shows a systematic, data-driven approach.
Introduction to the Performance Tab
The Performance tab in Chrome DevTools is a profiler that records everything happening in the browser during a time window. It captures CPU activity, network requests, rendering work, and memory usage — all on a synchronized timeline.
What It Records
CPU Activity
Every JavaScript function call, event handler, and browser task. Shown as colored blocks in the flame chart — the wider the block, the longer it took.
Network Requests
All HTTP requests with timing (DNS, connection, TTFB, download). Shows what's loading and when, helping identify waterfall bottlenecks.
Rendering Work
Layout (reflow), paint, and composite operations. Shows when the browser recalculates styles, positions elements, and draws pixels.
Memory Usage
JS heap size over time. Rising heap without drops indicates a memory leak. Sudden spikes show expensive allocations.
How to Open It
macOS: Cmd + Option + I → Performance tab Windows: F12 or Ctrl + Shift + I → Performance tab Keyboard shortcut to start recording: Cmd + Shift + E (macOS) Ctrl + Shift + E (Windows)
When to use Performance tab vs Lighthouse
Use Lighthouse for a quick automated audit with scores and recommendations. Use the Performance tab when you need to debug a specific interaction — it gives you the raw timeline data to pinpoint exactly which function or operation is slow.
Recording a Performance Trace
A good recording captures the exact interaction you want to analyze — nothing more, nothing less. Here's the step-by-step process.
Open DevTools → Performance tab
Right-click → Inspect, or use Cmd+Option+I (macOS). Navigate to the Performance tab. You'll see a record button (⏺) and options for CPU/network throttling.
Configure settings (optional but recommended)
Enable 'Screenshots' to see visual snapshots during the trace. Enable 'CPU throttling' (4x or 6x slowdown) to simulate real user devices. Check 'Network throttling' for slow connection testing.
Click Record (⏺) or press Cmd+Shift+E
The recording starts. DevTools is now capturing everything — CPU, network, rendering, memory. The tab shows a red dot indicating active recording.
Perform the interaction you want to analyze
Click the slow button, scroll the laggy page, or navigate to the slow route. Keep the recording focused — 3-5 seconds is usually enough. Don't record idle time.
Stop recording
Click the stop button or press Cmd+Shift+E again. DevTools processes the trace and displays the timeline. This may take a few seconds for long recordings.
Analyze the results
Zoom into the area of interest (the interaction). Look at the flame chart for long tasks, the summary panel for time breakdown, and the FPS row for frame drops.
Pro tip: Record page load
Click the reload button (↻) next to the record button to automatically record a full page load. This captures everything from navigation start to load complete — perfect for analyzing FCP, LCP, and initial render performance.
Understanding the Performance Panel
The Performance panel has several lanes stacked vertically. Each lane shows a different aspect of what the browser was doing. Here's how to read each one.
A. Timeline Overview (Top Bar)
The top bar gives you a bird's-eye view of the entire recording.
┌─────────────────────────────────────────────────────┐ │ FPS ████████░░░░████████████░░░████████████ │ │ green = good (60fps), red = dropped frames│ ├─────────────────────────────────────────────────────┤ │ CPU ▓▓▓░░░▓▓▓▓▓▓▓▓░░░░▓▓▓▓▓▓░░░░░░░░░░░░░ │ │ yellow=JS purple=layout green=paint │ ├─────────────────────────────────────────────────────┤ │ NET ────── ──── ────────── ─── │ │ blue bars = network requests │ ├─────────────────────────────────────────────────────┤ │ Screenshots [📸] [📸] [📸] [📸] [📸] [📸] │ │ visual snapshots at each point in time │ └─────────────────────────────────────────────────────┘
| Lane | What It Shows | What to Look For |
|---|---|---|
| FPS | Frames per second over time | Red bars = dropped frames = jank |
| CPU | CPU usage colored by category | Tall yellow blocks = heavy JS execution |
| NET | Network request timeline | Long bars = slow downloads blocking render |
| Screenshots | Visual snapshots of the page | When content first appears (FCP/LCP) |
B. Main Thread
The Main thread lane is where you'll spend most of your time. It shows every task the browser executed on the main thread — JavaScript, layout, paint, garbage collection, and more.
Main ───────────────────────────────────────────────── │ │ │ ┌──────────────────────┐ ┌────┐ ┌──────────────┐ │ │ │ Task (150ms) │ │Task│ │ Task (80ms) │ │ │ │ ┌────────────────┐ │ │20ms│ │ ┌──────────┐ │ │ │ │ │ onClick() │ │ └────┘ │ │ render() │ │ │ │ │ │ ┌────────────┐ │ │ │ └──────────┘ │ │ │ │ │ │ fetchData()│ │ │ └──────────────┘ │ │ │ │ └────────────┘ │ │ │ │ │ └────────────────┘ │ │ │ └──────────────────────┘ │ │ ▲ │ │ Red striped = Long Task (>50ms) │ └─────────────────────────────────────────────────────┘
C. Flame Chart
The flame chart is a visualization of the call stack over time. Each bar is a function call. Width = duration. Depth = call stack depth. The wider a bar, the longer that function took.
Time ──────────────────────────────────────────► ┌─────────────────────────────────────────────┐ │ Task (200ms) │ ← Top-level task │ ┌───────────────────────────────────────┐ │ │ │ handleClick() (180ms) │ │ ← Event handler │ │ ┌─────────────────┐ ┌────────────┐ │ │ │ │ │ processData() │ │ updateDOM()│ │ │ ← Called functions │ │ │ (120ms) │ │ (50ms) │ │ │ │ │ │ ┌─────────────┐ │ └────────────┘ │ │ │ │ │ │ sortArray() │ │ │ │ ← Deeper call │ │ │ │ (100ms) │ │ │ │ │ │ │ └─────────────┘ │ │ │ │ │ └─────────────────┘ │ │ │ └───────────────────────────────────────┘ │ └─────────────────────────────────────────────┘ How to read: • Width = time spent (wider = slower) • Depth = call stack (deeper = more nested calls) • Color = category (yellow=JS, purple=layout, green=paint) • Click any bar to see details in the Summary panel
D. Summary Panel
Click any task or function in the flame chart to see a breakdown in the Summary panel at the bottom. It shows time spent in each category.
| Category | Color | What It Means |
|---|---|---|
| Scripting | JavaScript execution — function calls, event handlers, timers | |
| Rendering | Style recalculation and layout (reflow). Expensive if frequent. | |
| Painting | Filling pixels — drawing text, images, borders, shadows | |
| System / Idle | Browser overhead or idle time. Idle is good — means the thread is free. |
The most important skill
Learning to read the flame chart is the single most valuable DevTools skill. It tells you exactly which function is slow, how long it took, and what called it. Practice by recording traces on real sites and exploring the chart.
Identifying Performance Issues
Once you have a trace, here's what to look for. These are the most common performance issues and how they appear in DevTools.
Long Tasks (>50ms)
Any task over 50ms is flagged as a "Long Task" with a red striped corner in the flame chart. Long tasks block user input and rendering — they're the #1 cause of sluggish interactions.
Main Thread: ┌──────────────────────────────────────┐ │ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ │ ← 200ms task (RED corner) │ Long Task │ │ └── expensiveFunction() (180ms) │ ← This is your bottleneck └──────────────────────────────────────┘ What to do: 1. Click the long task to see which function is responsible 2. Look at the deepest/widest bar — that's where time is spent 3. Optimize that function or break it into smaller chunks
Blocking JavaScript
Large yellow blocks in the CPU chart or flame chart indicate heavy JavaScript execution. Common causes: large bundle evaluation, expensive computations, or synchronous data processing.
Layout Thrashing
Look for repeated purple "Layout" blocks interleaved with yellow "Scripting" blocks. This pattern means code is reading layout properties and writing styles in a loop, forcing the browser to recalculate layout on every iteration.
Main Thread (zoomed in): ┌────────┐┌──────┐┌────────┐┌──────┐┌────────┐ │ JS ││Layout││ JS ││Layout││ JS │ ... │(write) ││(calc)││(write) ││(calc)││(write) │ └────────┘└──────┘└────────┘└──────┘└────────┘ ↑ This alternating pattern = layout thrashing Fix: batch reads, then batch writes
Excessive React Re-renders
In React apps, look for repeated function calls to component render functions in the flame chart. Use the React DevTools Profiler alongside the Performance tab to see which components re-rendered and why.
Quick diagnosis checklist
Red FPS bar? → Dropped frames, likely layout or paint issue. Wide yellow block? → Heavy JS, find the function in the flame chart. Purple blocks? → Layout/reflow, check for thrashing. Green blocks? → Paint, check for expensive CSS (shadows, filters).
Rendering & Layout Insights
DevTools shows you exactly when the browser performs layout, paint, and composite operations. Understanding these helps you optimize rendering performance.
Layout (Reflow) Indicators
Purple "Layout" blocks in the flame chart show when the browser recalculates element positions and sizes. Click a Layout block to see:
- →Layout scope — how many elements were affected (partial vs full document)
- →Layout root — which element triggered the recalculation
- →Elements affected — the count of nodes that needed repositioning
- →Layout forced — whether JavaScript forced a synchronous layout
Paint Events
Green "Paint" blocks show when the browser draws pixels. Enable "Paint flashing" in the Rendering drawer (Cmd+Shift+P → "Show Rendering") to see green rectangles flash over areas being repainted in real time.
Composite Layers
Open the Layers panel (Cmd+Shift+P → "Show Layers") to see which elements have their own compositor layer. Elements with will-change: transform or transform: translateZ(0) get promoted to their own layer for GPU-accelerated compositing.
| Rendering Phase | DevTools Indicator | What to Check |
|---|---|---|
| Style Recalculation | Purple 'Recalculate Style' block | Too many affected elements? Simplify selectors. |
| Layout (Reflow) | Purple 'Layout' block | 'Layout forced' warning? Fix layout thrashing. |
| Paint | Green 'Paint' block | Large paint area? Reduce box-shadows, clip-paths. |
| Composite | Green 'Composite Layers' block | Too many layers? Remove unnecessary will-change. |
Rendering drawer tools
Press Cmd+Shift+P and type "Rendering" to access: Paint flashing (highlights repaints), Layout shift regions (highlights CLS), Scrolling performance issues (flags non-composited scrollers), and Core Web Vitals overlay.
Real Example Walkthrough
Let's walk through a real debugging scenario step by step — the kind of analysis you'd do at work or demonstrate in an interview.
Scenario: Slow Button Click (400ms delay)
A user reports that clicking "Load Dashboard" feels sluggish. The data appears after a noticeable delay. Let's profile it.
// dashboard.js — the code we're profiling button.addEventListener("click", async () => { showSpinner(); const data = await fetch("/api/dashboard").then(r => r.json()); // ❌ Problem: processing 10,000 items synchronously const processed = data.items.map(item => { // Expensive computation per item return { ...item, score: calculateComplexScore(item), // ~0.03ms each formatted: formatCurrency(item.value), // ~0.01ms each }; }); // ❌ Problem: reading layout in a loop processed.forEach(item => { const el = document.getElementById(item.id); const height = el.offsetHeight; // Forces layout el.style.height = (height + 20) + "px"; // Invalidates layout }); hideSpinner(); });
What You See in DevTools
FPS: ████████░░░░░░░░░░░░░░░░░░████████████████ 60fps ← dropped frames → 60fps restored CPU: ░░░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░░ ← heavy JS + layout → Main Thread: ┌──────────────────────────────────────────────────┐ │ Task (380ms) ⚠️ LONG TASK │ │ ┌──────────────────────────────────────────────┐ │ │ │ click handler │ │ │ │ ┌──────────┐ ┌──────────────────────────────┐│ │ │ │ │ fetch() │ │ anonymous (processing) 280ms││ │ │ │ │ 100ms │ │ ┌────────────────────────┐ ││ │ │ │ │ (network)│ │ │ .map() loop 180ms │ ││ │ │ │ └──────────┘ │ └────────────────────────┘ ││ │ │ │ │ ┌────────────────────────┐ ││ │ │ │ │ │ .forEach() + Layout │ ││ │ │ │ │ │ thrashing 100ms │ ││ │ │ │ │ └────────────────────────┘ ││ │ │ │ └──────────────────────────────┘│ │ │ └──────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────┘ Summary Panel: Scripting: 280ms (73%) Rendering: 100ms (26%) ← layout thrashing! Painting: 4ms (1%)
Diagnosis
- ⚠Long Task: 380ms total — well over the 50ms threshold
- ⚠Bottleneck 1: .map() loop processing 10,000 items synchronously (180ms)
- ⚠Bottleneck 2: .forEach() with layout thrashing — reading offsetHeight then writing style.height in a loop (100ms)
- ⚠FPS drops to 0 during the task — UI is completely frozen
The Fix
button.addEventListener("click", async () => { showSpinner(); const data = await fetch("/api/dashboard").then(r => r.json()); // ✅ Fix 1: Process in chunks with setTimeout to yield const processed = []; for (let i = 0; i < data.items.length; i += 500) { const chunk = data.items.slice(i, i + 500); processed.push(...chunk.map(item => ({ ...item, score: calculateComplexScore(item), formatted: formatCurrency(item.value), }))); // Yield to event loop every 500 items if (i + 500 < data.items.length) { await new Promise(r => setTimeout(r, 0)); } } // ✅ Fix 2: Batch reads, then batch writes (no thrashing) const heights = processed.map(item => { const el = document.getElementById(item.id); return el.offsetHeight; // Batch all reads }); processed.forEach((item, i) => { const el = document.getElementById(item.id); el.style.height = (heights[i] + 20) + "px"; // Batch all writes }); hideSpinner(); });
After fixing: re-record
Always re-record a trace after optimizing. Verify the long task is gone, FPS stays at 60, and the Summary panel shows reduced scripting/rendering time. This confirms your fix actually worked — not just in theory.
Performance Optimization Workflow
Follow this systematic workflow every time you need to debug a performance issue. It's the same process used by performance engineers at top companies.
Reproduce the issue
Identify the exact interaction that's slow. 'The page is slow' is too vague. 'Clicking the filter dropdown takes 300ms to respond' is actionable.
Record a performance trace
Open DevTools Performance tab, enable CPU throttling (4x), start recording, perform the slow interaction, stop recording. Keep it focused — 3-5 seconds.
Identify the bottleneck
Look for long tasks (red corners) in the flame chart. Click the widest/deepest bar to find the slow function. Check the Summary panel for the time breakdown (scripting vs rendering vs painting).
Diagnose the root cause
Is it heavy JS? Break into chunks or move to a Web Worker. Layout thrashing? Batch reads and writes. Large paint? Simplify CSS. Too many re-renders? Memoize components.
Apply the fix
Make the targeted code change. Don't refactor everything — fix the specific bottleneck identified in the trace.
Re-record and verify
Record another trace of the same interaction. Confirm the long task is gone, FPS is stable, and the Summary shows improvement. If not, go back to step 3.
The workflow in one line
Reproduce → Record → Identify → Diagnose → Fix → Verify. Never skip the "Verify" step — assumptions about performance are often wrong.
Advanced Insights
These advanced features help you simulate real-world conditions and get deeper insights from your traces.
CPU Throttling
Simulate slower devices (4x or 6x slowdown). Your MacBook Pro is 5-10x faster than the average user's phone. Always test with throttling enabled.
Network Throttling
Simulate slow connections (3G, slow 4G). Reveals how network latency affects CRP and resource loading. Critical for testing real-world performance.
Screenshots / Filmstrip
Enable 'Screenshots' before recording to capture visual snapshots. Scrub through the timeline to see exactly when content appears — essential for FCP/LCP analysis.
Memory Timeline
Check 'Memory' before recording to see JS heap size over time. A steadily rising line without drops = memory leak. Sawtooth pattern = normal GC.
Other Useful DevTools Panels
| Panel / Tool | What It Does | When to Use |
|---|---|---|
| Lighthouse | Automated audit with scores for Performance, A11y, SEO | Quick health check, CI/CD integration |
| Coverage tab | Shows unused CSS/JS bytes per file | Finding dead code to remove or lazy-load |
| Network tab | Detailed request waterfall with timing breakdown | Debugging slow API calls, large downloads |
| Rendering drawer | Paint flashing, layout shift regions, FPS meter | Real-time visual debugging of rendering issues |
| React DevTools Profiler | Component render times, why components re-rendered | React-specific re-render debugging |
Cmd + Shift + P → Command palette (search any DevTools feature) Cmd + Shift + E → Start/stop Performance recording Cmd + Shift + M → Toggle device mode (responsive testing) Cmd + Shift + C → Inspect element mode In Performance tab: W / S → Zoom in / out on timeline A / D → Pan left / right Click + drag → Select a time range Ctrl + F → Search for function names in flame chart
Always throttle CPU
The most common mistake in performance testing is profiling on a fast developer machine. Enable 4x CPU throttling to see what your users actually experience. A 50ms task on your machine becomes 200ms on a mid-range phone.
Common Mistakes
These mistakes lead to wasted time, wrong conclusions, and missed optimizations.
Profiling on a fast machine without throttling
Your M2 MacBook is 5-10x faster than the average user's device. A task that takes 30ms on your machine takes 150ms+ on a mid-range Android phone. You'll miss real bottlenecks.
✅Always enable 4x CPU throttling in the Performance tab settings. Test on real devices when possible.
Looking at the wrong part of the trace
Recording 30 seconds of idle browsing then wondering why there are no issues. The trace is full of noise and the actual slow interaction is buried.
✅Keep recordings short and focused (3-5 seconds). Record only the specific interaction that's slow.
Focusing on total load time instead of user-perceived metrics
Total page load might be 5 seconds, but if FCP is 800ms and the page is interactive at 1.5s, users don't care about the rest loading in the background.
✅Focus on FCP, LCP, and INP (Interaction to Next Paint). These are what users feel.
Optimizing before measuring
Spending hours memoizing React components when the actual bottleneck is a 500KB CSS file blocking render. Without profiling, you're guessing.
✅Always record a trace first. Let the data tell you what's slow. Fix the biggest bottleneck first.
Not re-testing after optimization
Assuming your fix worked without verifying. Sometimes optimizations have no effect, or they introduce new issues (like layout shifts).
✅Always re-record a trace after fixing. Compare before/after. Verify the long task is gone.
Ignoring third-party scripts
Third-party scripts (analytics, chat widgets, A/B testing) often cause long tasks that you don't control. They show up in the flame chart but are easy to overlook.
✅Filter the flame chart by origin. Defer or lazy-load third-party scripts. Consider removing unused ones.
Interview Questions
Performance debugging questions test your practical skills. Interviewers want to hear a systematic approach, not just tool names.
Q:How do you debug a slow page or interaction?
A: I follow a systematic workflow: (1) Reproduce the exact slow interaction. (2) Open Chrome DevTools Performance tab with CPU throttling enabled. (3) Record a trace of the interaction. (4) Look for long tasks (>50ms) in the flame chart. (5) Click the widest bar to identify the slow function. (6) Check the Summary panel for time breakdown (scripting vs rendering). (7) Fix the bottleneck and re-record to verify.
Q:What is a flame chart and how do you read it?
A: A flame chart visualizes the call stack over time. Each horizontal bar is a function call — width represents duration, depth represents call stack depth. The widest bars at the deepest level are your bottlenecks. Colors indicate category: yellow for JavaScript, purple for layout/style, green for paint. Click any bar to see details in the Summary panel.
Q:What is a long task and why does it matter?
A: A long task is any task on the main thread that takes more than 50ms. During a long task, the browser can't respond to user input or update the screen — the page feels frozen. Long tasks directly impact INP (Interaction to Next Paint). They appear with a red striped corner in the DevTools flame chart.
Q:How do you identify layout thrashing in DevTools?
A: Layout thrashing appears as alternating purple (Layout) and yellow (Scripting) blocks in the flame chart. It means code is reading layout properties (offsetHeight) and writing styles in a loop, forcing synchronous layout on every iteration. The Layout block will show a 'Layout forced' warning. Fix by batching all reads before all writes.
Q:What tools do you use for frontend performance analysis?
A: Chrome DevTools Performance tab for detailed profiling of specific interactions. Lighthouse for automated audits with scores. WebPageTest for real-device testing on various connections. React DevTools Profiler for component-level render analysis. The Coverage tab for finding unused CSS/JS. The Network tab for request waterfall analysis.
Q:How do you measure Core Web Vitals?
A: FCP and LCP appear in the Performance tab timeline (look for the markers). Lighthouse reports all Core Web Vitals with scores. The web-vitals npm library measures them in production with real user data (RUM). Chrome's Rendering drawer has a Core Web Vitals overlay for real-time monitoring during development.
Q:What's the difference between the Performance tab and Lighthouse?
A: Lighthouse runs an automated audit and gives you scores with recommendations — great for quick health checks. The Performance tab records a raw timeline of everything the browser does — great for debugging specific interactions. Use Lighthouse to find what's wrong, use the Performance tab to understand why and find the exact code responsible.
Q:How would you debug excessive React re-renders?
A: Use React DevTools Profiler to record a trace, then check which components re-rendered and why (props changed, state changed, parent re-rendered). In the Chrome Performance tab, look for repeated calls to component functions in the flame chart. Common fixes: React.memo for pure components, useMemo/useCallback for expensive computations and stable references, state colocation to reduce re-render scope.
Practice Section
These scenarios test your ability to connect symptoms to root causes using DevTools — exactly what interviewers and real debugging require.
Slow Click Response
A user clicks 'Submit' and nothing happens for 500ms. The flame chart shows a single 480ms yellow block labeled 'validateForm()'. How do you fix this?
Answer: The validateForm() function is a long task blocking the main thread for 480ms. Options: (1) If it's doing heavy computation (regex on large text, complex validation), break it into chunks with setTimeout or scheduler.yield(). (2) If it's synchronous API calls, make them async with await. (3) If it's validating many fields, validate only changed fields incrementally. (4) Show a loading state immediately (before validation starts) so the user gets feedback. Re-record to verify the task is under 50ms.
Blocking Main Thread
The Performance trace shows the main thread is 100% busy for 2 seconds during page load. The CPU chart is solid yellow. The Summary shows 'Scripting: 1.8s'. What's happening and how do you investigate?
Answer: 1.8s of scripting during load means a large JS bundle is being evaluated synchronously. Steps: (1) Look at the flame chart — find the widest top-level bar, it's likely 'Evaluate Script' for a large bundle. (2) Check the Network tab for bundle sizes. (3) Use the Coverage tab to find unused JS. Fix: code-split with dynamic import(), defer non-critical scripts, tree-shake unused exports. Target: no single script evaluation over 50ms.
Janky Scroll
A page with a long list scrolls at 15fps instead of 60fps. The FPS bar is mostly red. The flame chart shows repeated purple 'Layout' blocks during scroll. What's the issue?
Answer: Repeated Layout blocks during scroll = something is triggering reflow on every frame. Common causes: (1) A scroll event handler reading layout properties (offsetTop) — fix with IntersectionObserver instead. (2) position: sticky or fixed elements causing full-page layout — check with the Layers panel. (3) Non-composited animations (animating top/left instead of transform). Fix: use transform for animations, debounce scroll handlers, use CSS contain: layout on list items, virtualize the list if it has 1000+ items.
Cheat Sheet (Quick Revision)
One-screen reference for performance debugging.
Quick Revision Cheat Sheet
Workflow: Reproduce → Record → Identify → Diagnose → Fix → Verify.
Long Task: >50ms on main thread. Red striped corner in flame chart. Blocks input + rendering.
Flame Chart: Width = duration, depth = call stack. Widest deepest bar = bottleneck.
Colors: Yellow = JS scripting. Purple = layout/style. Green = paint. Gray = idle.
CPU throttling: Always enable 4x slowdown. Your machine is 5-10x faster than user devices.
Layout thrashing: Alternating JS + Layout blocks. Fix: batch reads, then batch writes.
FPS bar: Green = 60fps (good). Red = dropped frames (jank). Target: no red during interactions.
Summary panel: Click any task to see time breakdown: Scripting, Rendering, Painting, Idle.
Screenshots: Enable before recording. Scrub timeline to see when content appears (FCP/LCP).
Coverage tab: Shows unused CSS/JS per file. Red = unused bytes. Remove or lazy-load.
Paint flashing: Rendering drawer → Paint flashing. Green rectangles show repainted areas.
React profiling: React DevTools Profiler shows which components re-rendered and why.
Key shortcuts: Cmd+Shift+E = record. W/S = zoom. A/D = pan. Ctrl+F = search flame chart.