{"id":"lb8pucjzpwv2tqr","title":"Website Performance Audit Checklist (2026): What to Fix First on a React Site","slug":"website-performance-audit-2026-react","summary":"I audit React marketing sites in this order: real URL field data first, then LCP and head order, then crawl HTML and INP — with curl and Lighthouse commands you can run today.","imageUrl":"https://briancrabtree.me/images/journal-website-performance-audit-2026-react.webp","category":"Performance","date":"2026-06-11T18:00:00.000Z","featured":false,"likes":10,"author":"Brian Crabtree","content":"<h2>A green Lighthouse score in Chrome is not an audit</h2>\n\n<p>Running PageSpeed Insights on localhost, or on your laptop over Wi‑Fi, is a smoke test — not a website performance audit. Real users hit production over mobile networks, through your CDN, with your analytics scripts and fonts. Google’s ranking signals mix <strong>lab</strong> data (Lighthouse) with <strong>field</strong> data (Chrome UX Report) when enough traffic exists.</p>\n\n<p>I wrote about that split in <a href=\"/journal/why-pagespeed-scores-change-every-run/\">Why PageSpeed Scores Change Every Run</a> and <a href=\"/journal/lab-vs-field-data-pagespeed/\">Lab vs Field Data — When PageSpeed Scores Mislead You</a>. This post is the ordered checklist I run on React marketing sites before handoff — and before I tell a client their stack is “fine.”</p>\n\n<h2>Phase 0 — Audit the real URL</h2>\n\n<p>Start with the canonical production URL. Not staging. Not <code>localhost</code>.</p>\n\n<pre><code># Quick production sanity\ncurl -sI \"https://yoursite.com/\" | rg -i 'HTTP/|content-type|cache-control'\n\n# PageSpeed lab (replace URL)\n# https://pagespeed.web.dev/analysis?url=https://yoursite.com/</code></pre>\n\n<p>Record mobile and desktop lab scores. If CrUX field data exists in PSI, compare LCP, INP, and CLS field percentiles to lab. When they diverge, trust field for user pain and lab for reproducible fixes. See <a href=\"/journal/performance-checks-before-handoff/\">Performance Checks Before Handoff</a> for how I document what changed.</p>\n\n<h2>Phase 1 — LCP: head order before framework blame</h2>\n\n<p>On Vite and Create React App builds, the default HTML often places <code>&lt;script type=\"module\"&gt;</code> before the main stylesheet. The browser executes JS, React hydrates, and users see an unstyled flash. Lab LCP suffers. This is fixable without rewriting the app.</p>\n\n<pre><code>&lt;link rel=\"preload\" href=\"/assets/index-HASH.css\" as=\"style\"&gt;\n&lt;link rel=\"stylesheet\" href=\"/assets/index-HASH.css\"&gt;\n&lt;script type=\"module\" src=\"/assets/index-HASH.js\"&gt;&lt;/script&gt;</code></pre>\n\n<p>Never defer main CSS while module JS runs first on marketing pages. Full write-up: <a href=\"/journal/react-css-before-module-js-pagespeed/\">Why Your React Site Loads Unstyled (CSS Before Module JS)</a>. Pair with hero image discipline in <a href=\"/journal/image-srcset-lcp-sizing/\">Image srcset and LCP Sizing</a>.</p>\n\n<h2>Phase 2 — CLS and fonts</h2>\n\n<p>Layout shift usually comes from late fonts, unsized images, or hydration inserting content above the fold. Audit:</p>\n\n<ul>\n  <li><code>font-display</code> strategy and preloaded WOFF2 for critical weights</li>\n  <li>Explicit <code>width</code> / <code>height</code> or aspect-ratio on LCP image</li>\n  <li>No client-only inserts above the hero without reserved space</li>\n</ul>\n\n<p>Deeper dives: <a href=\"/journal/font-loading-without-layout-shift/\">Font Loading Without Layout Shift</a>, <a href=\"/journal/hydration-cls-react-fixes/\">Hydration CLS Fixes for React</a>.</p>\n\n<h2>Phase 3 — INP and main-thread debt</h2>\n\n<p>Interaction to Next Paint replaced FID. Slow buttons and menus are long tasks on the main thread — not “maybe the server.” Profile a click in Performance panel; find tasks over 200ms. Defer non-critical work, split handlers, avoid synchronous JSON parse on click.</p>\n\n<p>See <a href=\"/journal/interaction-to-next-paint-inp-guide/\">Interaction to Next Paint (INP) Guide</a>. Third-party widgets often dominate: <a href=\"/journal/tag-manager-performance-tax/\">Tag Manager Performance Tax</a>.</p>\n\n<h2>Phase 4 — SEO crawl audit (SPAs are not exempt)</h2>\n\n<p>Performance and SEO share the same first HTML response. If Google gets a thin shell, you can pass Lighthouse and still fail indexing.</p>\n\n<pre><code>curl -sL \"https://yoursite.com/journal/my-post/\" \\\n  | rg -i 'canonical|prerender-post-prose|&lt;h2'</code></pre>\n\n<p>You want per-URL canonical, article prose in the initial HTML, and trailing-slash consistency. Failure modes: <a href=\"/journal/journal-soft-404-prerender-body/\">Soft 404 on prerender shells</a>, <a href=\"/journal/react-spa-crawled-not-indexed-fix/\">React SPA crawled not indexed</a>, full audit framing in <a href=\"/journal/technical-seo-audit-react-spa/\">Technical SEO Audit for React SPAs</a>. After fixes ship, feed discovery with internal links — <a href=\"/journal/internal-links-spa-journal-discovery/\">How I Feed Google New Field Notes Without Another Deploy</a>.</p>\n\n<h2>Phase 5 — Edge cache and deploy hygiene</h2>\n\n<p>Static assets with <code>Set-Cookie</code> on every response can force CDN BYPASS. Users re-download the same hashed chunk on every navigation. See <a href=\"/journal/cloudflare-set-cookie-bypasses-edge-cache/\">Cloudflare Set-Cookie Bypasses Edge Cache</a> and <a href=\"/journal/cloudflare-cache-stale-assets-after-deploy/\">Stale Assets After Deploy</a>.</p>\n\n<h2>Copy-paste audit checklist</h2>\n\n<pre><code>- [ ] PSI mobile + desktop on production URL (not localhost)\n- [ ] Compare lab LCP/INP/CLS to CrUX field when available\n- [ ] Main CSS preload + blocking link BEFORE module script\n- [ ] LCP image: dimensions, fetchpriority, modern format (webp/avif)\n- [ ] Fonts: preload critical files; no invisible-text swap on hero\n- [ ] INP: no long tasks on primary CTAs\n- [ ] curl: canonical + prerender prose on key templates\n- [ ] Sitemap URLs match canonical trailing slashes\n- [ ] CDN cache HIT on hashed static assets\n- [ ] Document fixes before handoff (no invented scores)</code></pre>\n\n<p>For a shorter SMB-oriented list, see <a href=\"/journal/website-performance-audit-checklist/\">Website Performance Audit Checklist</a>. Live reference: <a href=\"https://briancrabtree.me/\">briancrabtree.me</a> — I publish lab scores from PageSpeed Insights only, not datacenter Docker runs.</p>\n\n<h2>When to hire this out</h2>\n\n<p>If your audit stalls at “we need a redesign” but the real issues are head order, crawl HTML, and cache headers, you do not need a six-month rebuild. Send your production URL and PSI link at <a href=\"/contact?ref=journal-audit\">/contact</a>. See <a href=\"/services/\">services</a> for how I scope React performance and SEO hygiene.</p>","tags":["pagespeed-insights","core-web-vitals","lighthouse","react","website-audit","performance-audit"],"views":28}