| name | debug-performance |
| description | Diagnoses and fixes performance problems in web apps: slow renders, memory leaks, large bundles, waterfall requests, and poor Core Web Vitals. Use when the user says "my app is slow", "too many re-renders", "memory leak", "LCP is bad", "bundle is too large", "this feels laggy", "optimize this component", or shares performance profiler output or Lighthouse results. |
Debug Performance
Diagnoses web application performance problems across four layers: rendering,
network, bundle, and memory. Identifies the root cause, not just symptoms,
and provides concrete fixes with measurable impact.
Required Details
| Detail | Example |
|---|
| Symptom | "Laggy UI", "slow LCP", "memory grows over time" |
| Framework | Next.js / React / vanilla JS |
| Code or profiler output | Component, network waterfall, Lighthouse report |
| Where it's slow | Initial load / interaction / specific page |
Instructions
Step 1 — Identify the layer
| Symptom | Likely layer |
|---|
| Laggy UI, janky interactions | Rendering — re-renders or layout thrash |
| Slow initial page load | Network waterfall or bundle size |
| Bad LCP / CLS / INP score | Core Web Vitals — specific root causes per metric |
| Memory grows without release | Memory leak |
| Slow after many interactions | Memory leak or accumulating state |
| Slow API responses affecting UI | Network — waterfall or missing cache |
Step 2 — Diagnose by layer
Rendering (React)
let renderCount = 0;
console.log('renders:', ++renderCount);
<Child config={{ theme: 'dark' }} />
const config = useMemo(() => ({ theme: 'dark' }), []);
<Child config={config} />
Common causes:
- Props with inline objects/arrays/functions (
onClick={() => ...})
- Missing
React.memo on expensive pure components
- State too high — move state down to the component that needs it
useEffect with missing or wrong dependencies causing loops
Bundle size
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
const HeavyChart = dynamic(() => import('@/components/HeavyChart'), {
loading: () => <Skeleton />,
ssr: false,
});
import _ from 'lodash';
import debounce from 'lodash/debounce';
Core Web Vitals
| Metric | Root cause | Fix |
|---|
| LCP (slow largest paint) | Unoptimized hero image | <Image priority />, preload, WebP/AVIF |
| CLS (layout shift) | Images without dimensions | Always set width + height on images |
| INP (slow interaction) | Heavy JS on main thread | startTransition, useDeferredValue, Web Workers |
Memory leaks
useEffect(() => {
window.addEventListener('resize', handler);
}, []);
useEffect(() => {
window.addEventListener('resize', handler);
return () => window.removeEventListener('resize', handler);
}, []);
Common memory leaks:
- Event listeners without cleanup in
useEffect
setInterval / setTimeout without clearInterval / clearTimeout
- Subscriptions (WebSocket, RxJS, EventEmitter) not unsubscribed
- Closures holding references to large objects
Network waterfall
const user = await getUser(id);
const posts = await getPosts(user.id);
const [user, posts] = await Promise.all([
getUser(id),
getPosts(id),
]);
Step 3 — Verify the fix
For each fix, provide a way to confirm it worked:
- Re-renders: React DevTools Profiler → check render count dropped
- Bundle:
next build output → check chunk sizes
- LCP/CLS/INP: Lighthouse re-run → check score improvement
- Memory: Chrome DevTools Memory tab → heap snapshot before/after
Non-Negotiable Acceptance Criteria
Output Format
## Performance Diagnosis
### Root Cause
[What is actually causing the problem — be specific]
### Layer: [Rendering / Bundle / Network / Memory]
**Problem**
[explanation + problematic code]
**Fix**
[corrected code]
**How to verify**
[tool and what to look for]
[repeat for each layer affected]
### Expected Impact
[rough estimate: "should reduce re-renders by ~80%", "LCP improvement of ~1s"]