Web Vitals Analyzer
Overview
Diagnoses Core Web Vitals failures and frontend performance issues by analyzing page structure, resource loading patterns, JavaScript bundle composition, and rendering behavior. Produces prioritized fixes with estimated metric improvements.
Instructions
When asked to optimize frontend performance or Core Web Vitals:
Gather the current state — ask for or analyze:
- Lighthouse report (JSON or screenshot)
- The page URL or source code (HTML, CSS, JS entry points)
- Framework used (React, Next.js, Vue, vanilla, etc.)
- Current LCP, CLS, and INP values if available
Analyze Largest Contentful Paint (LCP) — target < 2.5s:
- Identify the LCP element (usually hero image, heading, or above-fold content)
- Check: Is the LCP image lazy-loaded? (it shouldn't be)
- Check: Is there a
<link rel="preload"> for the LCP resource?
- Check: Are render-blocking CSS/JS delaying first paint?
- Check: Server response time (TTFB) — if > 800ms, it's a server issue
- Check: Font loading strategy — is
font-display: swap or optional used?
- Check: Are critical CSS styles inlined or blocking?
Analyze Cumulative Layout Shift (CLS) — target < 0.1:
- Identify elements causing layout shifts (images without dimensions, dynamic content, ads, web fonts)
- Check: Do all
<img> and <video> tags have explicit width and height?
- Check: Is content injected above existing content after load?
- Check: Are web fonts causing FOUT/FOIT shifts?
- Check: Are dynamic ads or embeds reserving space with
aspect-ratio or fixed containers?
Analyze Interaction to Next Paint (INP) — target < 200ms:
- Identify heavy JavaScript on the main thread
- Check: Are event handlers doing synchronous layout thrashing?
- Check: Long tasks (> 50ms) blocking the main thread
- Check: Is there unnecessary re-rendering (React: missing memoization, excessive state updates)?
- Check: Are third-party scripts blocking interaction (analytics, chat widgets, A/B testing)?
- Suggest:
requestIdleCallback, scheduler.yield(), web workers for heavy computation
Analyze bundle and resource loading:
- Identify total JS bundle size and largest chunks
- Check for unused JavaScript (coverage analysis)
- Suggest code splitting opportunities (route-based, component-based)
- Check image formats (suggest WebP/AVIF for photos, SVG for icons)
- Identify duplicate dependencies in the bundle
Output format:
- Current scores vs targets for each metric
- Prioritized list of fixes (highest impact first)
- For each fix: what to change, where in the code, expected improvement
- A "quick wins" section for changes that take < 15 minutes
Examples
Example 1: Poor LCP on a marketing site
Input:
Our landing page scores 38 on Lighthouse performance. LCP is 5.2s. Here's the page source.
Output:
Core Web Vitals Diagnosis
==========================
LCP: 5.2s (target: < 2.5s) ❌
CLS: 0.03 (target: < 0.1) ✓
INP: N/A (no interaction data)
Root Causes (ranked by impact):
1. CRITICAL: Hero image (2.4MB JPEG) is lazy-loaded
File: src/components/Hero.tsx:12
Fix: Remove loading="lazy" from the above-fold hero image.
Add: <link rel="preload" as="image" href="/hero.jpg" fetchpriority="high">
Convert to WebP: reduces 2.4MB → 340KB
Expected LCP improvement: -2.1s
2. HIGH: 3 render-blocking CSS files (total 180KB uncompressed)
Files: fonts.css (89KB), animations.css (52KB), above-fold.css (39KB)
Fix: Inline critical CSS (above-fold.css) in <head>.
Load fonts.css and animations.css with media="print" onload hack.
Expected LCP improvement: -0.8s
3. MODERATE: Google Fonts loaded via @import in CSS (parser-blocking)
Fix: Switch to <link rel="preconnect" href="https://fonts.gstatic.com">
and <link rel="stylesheet" href="fonts-url"> in HTML <head>
Expected LCP improvement: -0.3s
Quick wins (< 15 min each):
• Add fetchpriority="high" to hero image tag
• Add width/height to hero image (already good CLS, but defensive)
• Enable text compression (gzip/brotli) on server — saves 60% on CSS
Example 2: High INP on a React dashboard
Input:
Our dashboard app has INP of 890ms. Users complain clicks feel laggy. It's a React 18 app with Zustand state management.
Output:
INP Diagnosis: 890ms (target: < 200ms) ❌
Root Causes:
1. CRITICAL: Filter dropdown triggers full data grid re-render (45,000 rows)
File: src/components/DataGrid.tsx
The grid component re-renders on every filter change because the parent
passes a new array reference each time.
Fix: Memoize the filtered data with useMemo:
const filteredData = useMemo(() => data.filter(filterFn), [data, filters]);
Wrap DataGrid in React.memo().
Expected INP improvement: -500ms
2. HIGH: Analytics script (third-party) runs 180ms synchronous task on click
Source: analytics.min.js:1 — event listener on document.body
Fix: Load analytics with async/defer. Move tracking to requestIdleCallback:
document.addEventListener('click', (e) => {
requestIdleCallback(() => trackClick(e));
});
Expected INP improvement: -180ms
3. MODERATE: Date formatting in table cells using Intl.DateTimeFormat per-render
Fix: Create formatter once outside component:
const fmt = new Intl.DateTimeFormat('en-US', { dateStyle: 'medium' });
Expected INP improvement: -80ms
Guidelines
- Always identify the specific LCP element — never give generic advice without knowing what's slow.
- CLS fixes must include explicit code changes (dimensions, aspect-ratio values, container styles).
- For INP, focus on the main thread. Network latency doesn't directly affect INP.
- Recommend
fetchpriority="high" only for the single most important above-fold resource.
- When suggesting image format changes, note that AVIF has better compression but slower decode — WebP is the safer default.
- Account for framework-specific patterns: Next.js Image component, Nuxt's
useAsyncData, SvelteKit's load functions.
- Don't recommend service workers for performance unless the use case specifically involves repeat visits.
1---2name: web-vitals-analyzer3description: Web Vitals Analyzer4---5# Web Vitals Analyzer67## Overview89Diagnoses Core Web Vitals failures and frontend performance issues by analyzing page structure, resource loading patterns, JavaScript bundle composition, and rendering behavior. Produces prioritized fixes with estimated metric improvements.1011## Instructions1213When asked to optimize frontend performance or Core Web Vitals:14151. **Gather the current state** — ask for or analyze:16 - Lighthouse report (JSON or screenshot)17 - The page URL or source code (HTML, CSS, JS entry points)18 - Framework used (React, Next.js, Vue, vanilla, etc.)19 - Current LCP, CLS, and INP values if available20212. **Analyze Largest Contentful Paint (LCP)** — target < 2.5s:22 - Identify the LCP element (usually hero image, heading, or above-fold content)23 - Check: Is the LCP image lazy-loaded? (it shouldn't be)24 - Check: Is there a `<link rel="preload">` for the LCP resource?25 - Check: Are render-blocking CSS/JS delaying first paint?26 - Check: Server response time (TTFB) — if > 800ms, it's a server issue27 - Check: Font loading strategy — is `font-display: swap` or `optional` used?28 - Check: Are critical CSS styles inlined or blocking?29303. **Analyze Cumulative Layout Shift (CLS)** — target < 0.1:31 - Identify elements causing layout shifts (images without dimensions, dynamic content, ads, web fonts)32 - Check: Do all `<img>` and `<video>` tags have explicit `width` and `height`?33 - Check: Is content injected above existing content after load?34 - Check: Are web fonts causing FOUT/FOIT shifts?35 - Check: Are dynamic ads or embeds reserving space with `aspect-ratio` or fixed containers?36374. **Analyze Interaction to Next Paint (INP)** — target < 200ms:38 - Identify heavy JavaScript on the main thread39 - Check: Are event handlers doing synchronous layout thrashing?40 - Check: Long tasks (> 50ms) blocking the main thread41 - Check: Is there unnecessary re-rendering (React: missing memoization, excessive state updates)?42 - Check: Are third-party scripts blocking interaction (analytics, chat widgets, A/B testing)?43 - Suggest: `requestIdleCallback`, `scheduler.yield()`, web workers for heavy computation44455. **Analyze bundle and resource loading:**46 - Identify total JS bundle size and largest chunks47 - Check for unused JavaScript (coverage analysis)48 - Suggest code splitting opportunities (route-based, component-based)49 - Check image formats (suggest WebP/AVIF for photos, SVG for icons)50 - Identify duplicate dependencies in the bundle51526. **Output format:**53 - Current scores vs targets for each metric54 - Prioritized list of fixes (highest impact first)55 - For each fix: what to change, where in the code, expected improvement56 - A "quick wins" section for changes that take < 15 minutes5758## Examples5960### Example 1: Poor LCP on a marketing site6162**Input:**63```64Our landing page scores 38 on Lighthouse performance. LCP is 5.2s. Here's the page source.65```6667**Output:**68```69Core Web Vitals Diagnosis70==========================71LCP: 5.2s (target: < 2.5s) ❌72CLS: 0.03 (target: < 0.1) ✓73INP: N/A (no interaction data)7475Root Causes (ranked by impact):76771. CRITICAL: Hero image (2.4MB JPEG) is lazy-loaded78 File: src/components/Hero.tsx:1279 Fix: Remove loading="lazy" from the above-fold hero image.80 Add: <link rel="preload" as="image" href="/hero.jpg" fetchpriority="high">81 Convert to WebP: reduces 2.4MB → 340KB82 Expected LCP improvement: -2.1s83842. HIGH: 3 render-blocking CSS files (total 180KB uncompressed)85 Files: fonts.css (89KB), animations.css (52KB), above-fold.css (39KB)86 Fix: Inline critical CSS (above-fold.css) in <head>.87 Load fonts.css and animations.css with media="print" onload hack.88 Expected LCP improvement: -0.8s89903. MODERATE: Google Fonts loaded via @import in CSS (parser-blocking)91 Fix: Switch to <link rel="preconnect" href="https://fonts.gstatic.com">92 and <link rel="stylesheet" href="fonts-url"> in HTML <head>93 Expected LCP improvement: -0.3s9495Quick wins (< 15 min each):96 • Add fetchpriority="high" to hero image tag97 • Add width/height to hero image (already good CLS, but defensive)98 • Enable text compression (gzip/brotli) on server — saves 60% on CSS99```100101### Example 2: High INP on a React dashboard102103**Input:**104```105Our dashboard app has INP of 890ms. Users complain clicks feel laggy. It's a React 18 app with Zustand state management.106```107108**Output:**109```110INP Diagnosis: 890ms (target: < 200ms) ❌111112Root Causes:1131141. CRITICAL: Filter dropdown triggers full data grid re-render (45,000 rows)115 File: src/components/DataGrid.tsx116 The grid component re-renders on every filter change because the parent117 passes a new array reference each time.118 Fix: Memoize the filtered data with useMemo:119 const filteredData = useMemo(() => data.filter(filterFn), [data, filters]);120 Wrap DataGrid in React.memo().121 Expected INP improvement: -500ms1221232. HIGH: Analytics script (third-party) runs 180ms synchronous task on click124 Source: analytics.min.js:1 — event listener on document.body125 Fix: Load analytics with async/defer. Move tracking to requestIdleCallback:126 document.addEventListener('click', (e) => {127 requestIdleCallback(() => trackClick(e));128 });129 Expected INP improvement: -180ms1301313. MODERATE: Date formatting in table cells using Intl.DateTimeFormat per-render132 Fix: Create formatter once outside component:133 const fmt = new Intl.DateTimeFormat('en-US', { dateStyle: 'medium' });134 Expected INP improvement: -80ms135```136137## Guidelines138139- Always identify the specific LCP element — never give generic advice without knowing what's slow.140- CLS fixes must include explicit code changes (dimensions, aspect-ratio values, container styles).141- For INP, focus on the main thread. Network latency doesn't directly affect INP.142- Recommend `fetchpriority="high"` only for the single most important above-fold resource.143- When suggesting image format changes, note that AVIF has better compression but slower decode — WebP is the safer default.144- Account for framework-specific patterns: Next.js Image component, Nuxt's `useAsyncData`, SvelteKit's load functions.145- Don't recommend service workers for performance unless the use case specifically involves repeat visits.