web performance checklist
Web Performance Checklist for Product Teams
Most web performance problems are known problems with known solutions. The challenge isn't discovery — it's systematically applying the fixes across a live product under normal engineering pressures.
This checklist is the one I run on every new project I join and every site I audit. Use it as a pre-launch review, a quarterly audit, or an onboarding document for new engineers.
How to Use This Checklist
Work through each section and mark items as Pass, Warning, or Fail. A Warning means the issue exists but isn't critical. A Fail means it's actively hurting performance or user experience.
Fix all Fails before launch. Address Warnings within the next sprint cycle.
Section 1: Images
- All hero and above-fold images use
priority(Next.js) orfetchpriority="high"(native) - All images have explicit
widthandheightattributes (prevents CLS) - All fill-mode images have a parent container with an explicit height
-
sizesattribute is set on all responsive images matching actual rendered sizes - Images use WebP or AVIF format (convert from PNG/JPG)
- No images wider than 2x the display size at any breakpoint
- Lazy loading is enabled for all below-fold images
- No decorative images lack
alt=""(empty alt for decorative, descriptive alt for informative) - No external images bypass the image optimisation pipeline (CDN or next/image)
Section 2: JavaScript
- Total initial JS bundle is under 200 KB gzipped
- No heavy libraries loaded at the layout level that are only needed in specific routes
- Animation libraries (Lottie, GSAP, Three.js) are lazy-loaded with
next/dynamic - Below-fold sections use dynamic imports to defer loading
- No
moment.js— usedate-fnswith tree-shaking - Icon libraries use named imports (not full library imports)
-
@next/bundle-analyzeror similar has been run within the last sprint - CI runs a bundle size check on every PR
- Source maps are generated in production for error tracking
Section 3: Fonts
- Fonts are loaded via
next/font(never via<link>to external CDN) -
font-display: swapis set on all fonts -
adjustFontFallback: trueis enabled (eliminates CLS during font swap) - No more than 2-3 font families loaded on any single page
- Font weights are limited to what's actually used (not loading all 9 weights)
- Variable fonts are used where possible (1 file covers all weights)
Section 4: Core Web Vitals
- LCP is under 2.5s on a mid-tier mobile device (Moto G Power or similar)
- INP is under 200ms for all primary interactions (click, type, navigate)
- CLS is under 0.1 across the full page load session
- LCP element is a server-rendered element (not blocked by JS)
- No layout-shifting animations (use only
transformandopacity) - Dynamic content (ads, banners, lazy sections) has reserved space via skeletons
- Cookie banners and notification prompts render in reserved space, not above content
Section 5: Server and Caching
- TTFB is under 600ms (faster is better — aim for under 200ms from cache)
- Static pages use
generateStaticParamsor ISR where appropriate - API routes have appropriate cache headers (
Cache-Control,ETag) - Static assets (images, fonts, JS) are served with long-lived cache headers
- A CDN is deployed in front of the origin server
-
Content-Encoding: br(Brotli) is used for text assets (JS, CSS, HTML)
Section 6: Third-Party Scripts
- Third-party scripts are loaded with
strategy="lazyOnload"orstrategy="afterInteractive"in Next.js - Google Analytics / Tag Manager does not block the main thread
- Chat widgets and support tools are deferred until after first interaction
- No render-blocking
<script>tags in<head>withoutdeferorasync - Total third-party JS is audited — remove anything not actively providing value
Section 7: HTML and CSS
- All pages have a single
<h1>with the primary keyword - No unused CSS is being loaded (run PurgeCSS or use Tailwind's built-in purge)
- Critical CSS is inlined (Next.js does this automatically in most cases)
- No CSS
@importchains (each@importis a separate blocking request) -
preconnecthints are set for important third-party domains
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://api.yourservice.com" />
Section 8: Monitoring
- Real User Monitoring (RUM) is collecting Core Web Vitals from production users
- Performance budget alerts are configured (notify when LCP/INP/CLS degrades)
- Synthetic monitoring runs Lighthouse against key pages on a schedule
- Google Search Console is set up and the site is verified
- Sitemap is submitted to Google Search Console
- Core Web Vitals report in Search Console is reviewed monthly
Section 9: Mobile
- Touch targets are at least 44x44 pixels
- Text is at least 16px (no
font-size: 12pxon body copy) - Viewport meta tag is set correctly:
<meta name="viewport" content="width=device-width, initial-scale=1"> - Horizontal scrolling does not occur at 375px viewport width
- Tap interactions feel immediate (no 300ms delay — handled by default in modern mobile browsers)
Quick Wins: Highest Impact, Lowest Effort
If you have 2 hours to improve performance, do these in order:
- Add
priorityto the hero image (5 minutes) - Switch fonts from
<link>tonext/font(30 minutes) - Check bundle analyser for obvious heavy modules (30 minutes)
- Add
sizesto the top 5 most-loaded images (30 minutes) - Move third-party scripts to
strategy="lazyOnload"(30 minutes)
These five changes typically improve LCP by 1–2 seconds and reduce initial JS by 20–40%.
FAQ
How often should I run this checklist? Before every major release and as a quarterly review. Set a calendar reminder — performance degrades gradually as new features ship, and a quarterly review catches it before it shows up in Search Console.
What tool should I use to run this checklist?
Start with PageSpeed Insights (real user data), Lighthouse in DevTools (synthetic, detailed), and @next/bundle-analyzer (bundle breakdown). For ongoing monitoring, use Vercel Analytics or a dedicated RUM tool.
Does performance really affect SEO? Yes. Core Web Vitals (LCP, INP, CLS) are confirmed Google ranking signals. Pages with Good CWV scores tend to rank higher than equivalent pages with Poor scores, all other signals equal.