Chrome DevToolsPerformanceFlame ChartProfilingDebugging

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.

30 min read14 sections
01

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.

02

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.

03

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

Opening the Performance Tabtext
macOS:  Cmd + Option + IPerformance tab
Windows: F12 or Ctrl + Shift + IPerformance 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.

04

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.

1

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.

2

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.

3

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.

4

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.

5

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.

6

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.

05

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.

Timeline Overview Lanestext

┌─────────────────────────────────────────────────────┐
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
└─────────────────────────────────────────────────────┘
LaneWhat It ShowsWhat to Look For
FPSFrames per second over timeRed bars = dropped frames = jank
CPUCPU usage colored by categoryTall yellow blocks = heavy JS execution
NETNetwork request timelineLong bars = slow downloads blocking render
ScreenshotsVisual snapshots of the pageWhen 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 Thread Visualizationtext

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.

Reading a Flame Charttext

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.

CategoryColorWhat It Means
ScriptingJavaScript execution — function calls, event handlers, timers
RenderingStyle recalculation and layout (reflow). Expensive if frequent.
PaintingFilling pixels — drawing text, images, borders, shadows
System / IdleBrowser 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.

06

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.

Spotting Long Taskstext
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 barthat'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.

Layout Thrashing in Flame Charttext
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).

07

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 PhaseDevTools IndicatorWhat to Check
Style RecalculationPurple 'Recalculate Style' blockToo many affected elements? Simplify selectors.
Layout (Reflow)Purple 'Layout' block'Layout forced' warning? Fix layout thrashing.
PaintGreen 'Paint' blockLarge paint area? Reduce box-shadows, clip-paths.
CompositeGreen 'Composite Layers' blockToo 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.

08

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.

The Slow Codejavascript
// 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

Flame Chart Analysistext
FPS:    ████████░░░░░░░░░░░░░░░░░░████████████████
        60fpsdropped 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

dashboard-optimized.jsjavascript
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.

09

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.

1

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.

2

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.

3

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).

4

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.

5

Apply the fix

Make the targeted code change. Don't refactor everything — fix the specific bottleneck identified in the trace.

6

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.

10

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 / ToolWhat It DoesWhen to Use
LighthouseAutomated audit with scores for Performance, A11y, SEOQuick health check, CI/CD integration
Coverage tabShows unused CSS/JS bytes per fileFinding dead code to remove or lazy-load
Network tabDetailed request waterfall with timing breakdownDebugging slow API calls, large downloads
Rendering drawerPaint flashing, layout shift regions, FPS meterReal-time visual debugging of rendering issues
React DevTools ProfilerComponent render times, why components re-renderedReact-specific re-render debugging
Useful DevTools Shortcutstext
Cmd + Shift + PCommand palette (search any DevTools feature)
Cmd + Shift + EStart/stop Performance recording
Cmd + Shift + MToggle device mode (responsive testing)
Cmd + Shift + CInspect element mode

In Performance tab:
  W / SZoom in / out on timeline
  A / DPan left / right
  Click + dragSelect a time range
  Ctrl + FSearch 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.

11

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.

12

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.

13

Practice Section

These scenarios test your ability to connect symptoms to root causes using DevTools — exactly what interviewers and real debugging require.

1

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.

2

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.

3

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.

14

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.