Nov 2026 · Checklist · ~11 min read
Web Performance Checklist 2026 — 20 Things to Check Before Launch
By Safdar Ali — frontend engineer, Bengaluru
I'm Safdar Ali, a frontend engineer in Bengaluru. Every launch week someone asks for a generic "web performance checklist." Here is mine — twenty checks I run before shipping marketing sites and portfolios on Next.js. I used this list on a client rebuild where LCP went from 4.2s to 1.7s; the full story is in my Next.js performance case study. This article is the checklist itself — copy it into your PR template.
Performance is a feature, not a phase. Marketing teams feel it first when bounce rate climbs on mobile — often before engineering notices. This web performance checklist is the document I attach to release tickets so nobody can say "we will optimise later." Later never comes unless metrics are gated. If you manage a team in Bengaluru shipping for global users, run the same checks on 4G throttling; desktop Wi-Fi in Koramangala offices lies about real user experience.
Why a checklist beats intuition
Performance regressions are rarely one giant mistake. They are ten small ones — an unoptimised hero image, a font blocking render, a client component fetching what the server should. A checklist forces you to look at categories you skip when tired. I run Lighthouse, WebPageTest, and real 4G throttling on a mid-range Android phone (Redmi class) because that is what a large share of Indian users actually use.
// BEFORE — ship when localhost feels fast
npm run build && vercel deploy
// AFTER — gate on metrics (example thresholds for marketing sites)
const launchGate = {
lcp: 2.5, // seconds, 75th percentile mobile
cls: 0.1,
inp: 200, // ms
jsBundle: 180, // KB gzip first load (app-specific)
};
// Block merge if CrUX field data exists and fails — lab alone liesThe 20-item web performance checklist
| # | Check | Target | Tool |
|---|---|---|---|
| 1 | LCP element identified | < 2.5s mobile | Lighthouse / CrUX |
| 2 | Hero image optimised | WebP/AVIF, width set | next/image |
| 3 | CLS from images/fonts | < 0.1 | Lighthouse |
| 4 | INP / interaction delay | < 200ms | Web Vitals extension |
| 5 | TTFB / server response | < 800ms | WebPageTest |
| 6 | Fonts self-hosted + subset | No layout shift | next/font |
| 7 | Third-party scripts audited | Defer non-critical | Coverage tab |
| 8 | JS bundle first load | Budget per route | @next/bundle-analyzer |
| 9 | Server vs client boundaries | Fetch on server | RSC audit |
| 10 | Caching headers / ISR | Stale-while-revalidate | Network panel |
| 11 | CDN for static assets | Edge near India | Vercel / Cloudflare |
| 12 | Lazy load below fold | loading="lazy" | Visual scroll test |
| 13 | Preconnect critical origins | API, CDN, fonts | HTML head |
| 14 | Compression Brotli/gzip | Enabled | curl -I |
| 15 | No render-blocking CSS | Critical CSS inlined | Lighthouse |
| 16 | API waterfall on page load | Parallel where possible | Network waterfall |
| 17 | 404/500 pages lightweight | No heavy JS | Manual |
| 18 | Service worker (if PWA) | Cache strategy documented | Application tab |
| 19 | Accessibility + perf overlap | Focus visible, alt text | axe |
| 20 | Post-launch monitoring | Alerts on LCP regression | Vercel Analytics / Sentry |
Print this table into your release PR description. Check boxes, paste Lighthouse screenshots. Future you will thank present you when marketing asks why traffic dropped.
Items 1–5 are Core Web Vitals — Google uses them in ranking signals alongside content quality. Items 6–14 are delivery: fonts, scripts, bundles, caching. Items 15–20 are process: errors, monitoring, accessibility overlap. Skip any bucket and you fix symptoms, not causes. I colour-code rows in our Notion release template: red blocks merge, yellow needs owner, green verified with screenshot.
LCP — find the real largest element
LCP is usually the hero image or a large text block with a web font. In Next.js, mark the LCP image with priority and correct sizes.
import Image from "next/image";
export function Hero() {
return (
<Image
src="/hero.webp"
alt="Product dashboard"
width={1200}
height={630}
priority
sizes="(max-width: 768px) 100vw, 1200px"
/>
);
}BEFORE: 2.1MB PNG hero, no sizes, LCP 4.2s. AFTER: WebP + priority + CDN, LCP 1.7s on the same page. Same layout, different delivery.
Identify the LCP element in Chrome DevTools → Performance → Experience section. If LCP is a text node, font loading is the culprit — subset weights, use next/font, avoid invisible text flashes. If LCP is an image below the fold, your priority flag is on the wrong asset. Hero video backgrounds are LCP killers on Indian networks; static poster image with lazy video is the compromise I recommend to clients.
CLS — reserve space before content arrives
// BEFORE — image without dimensions shifts layout
<img src="/banner.jpg" alt="" />
// AFTER — width/height or aspect-ratio reserves space
<div className="aspect-video w-full">
<Image src="/banner.webp" alt="Launch" fill className="object-cover" />
</div>Ads and cookie banners are CLS villains. Load them after LCP or reserve slot height. Indian users on Jio 4G notice every jump — they assume the site is broken.
Reserve space for dynamic ad slots if marketing injects them post-launch. CLS from ads caused a 0.18 score on a client site until we fixed min-height on the container — one line of CSS, weeks of confused analytics. Test with slow network and CPU 4x throttle in Lighthouse; layout shift that appears only under stress is still a launch blocker.
Bundle size — Server Components as default
Read RSC vs client components before launch. Every unnecessary "use client" ships React runtime for that subtree.
// BEFORE — entire page client for one counter
"use client";
export default function Page() {
const [n, setN] = useState(0);
const data = useFetchOnMount(); // also hurts SEO
return <Dashboard data={data} count={n} onInc={() => setN(n + 1)} />;
}
// AFTER — server fetch + tiny client leaf
export default async function Page() {
const data = await getDashboard();
return (
<>
<Dashboard data={data} />
<Counter />
</>
);
}API waterfall — parallel data on the server
Checklist item 16 is easy to miss: three serial fetches on the server still delay TTFB. In App Router pages, use Promise.all for independent requests — same pattern as client-side parallel fetch, but the win shows up in HTML arrival time, not only JSON in the browser.
// BEFORE — serial server fetches add latency to every page
export default async function Page() {
const header = await getHeader();
const posts = await getPosts();
const footer = await getFooter();
return <Layout header={header} posts={posts} footer={footer} />;
}
// AFTER — parallel where independent
export default async function Page() {
const [header, posts, footer] = await Promise.all([
getHeader(),
getPosts(),
getFooter(),
]);
return <Layout header={header} posts={posts} footer={footer} />;
}Fonts and third parties
import { Inter } from "next/font/google";
const inter = Inter({
subsets: ["latin"],
display: "swap",
variable: "--font-inter",
});
// Avoid: 6 weights × 2 families from Google CDN without subsetAudit GTM, Hotjar, chat widgets. Defer until after load or remove on mobile. One client removed an unused chat widget and INP improved 90ms — users could finally tap buttons on first try.
Caching and CDN — India latency
// app/blog/[slug]/page.tsx — ISR for content
export const revalidate = 3600;
async function getPost(slug: string) {
const res = await fetch("https://api.example.com/posts/" + slug, {
next: { revalidate: 3600 },
});
return res.json();
}Pick edge regions close to users. Bengaluru users hitting US-only origin without CDN add 200–400ms TTFB. Vercel and Cloudflare both work; configure consciously.
Set explicit cache headers on API routes that return public JSON. Private authenticated responses should not cache at CDN — stale user data is worse than slow TTFB. For static marketing pages, ISR with sensible revalidate windows means deploys propagate without rebuilding the entire site. Document your revalidate values in the PR so the next engineer knows why blog posts appear after 3600 seconds, not instantly.
INP and main-thread work — interactions that feel stuck
Interaction to Next Paint replaced FID as a Core Web Vital. Long tasks on the main thread — parsing huge bundles, synchronous JSON.parse on megabyte responses, layout thrashing — delay button feedback. Split heavy work with requestIdleCallback or move parsing to a Web Worker when you cannot reduce payload size.
// BEFORE — block main thread on click
function onExport() {
const csv = buildHugeCsv(rows); // 200ms sync
download(csv);
}
// AFTER — yield with scheduler (pattern; use worker for bigger jobs)
function onExport() {
requestAnimationFrame(() => {
const csv = buildHugeCsv(rows);
download(csv);
});
}How I test before merge
Local: Lighthouse mobile, React Profiler for avoidable re-renders. Staging: WebPageTest from Mumbai if available. Production: monitor CrUX after two weeks — field data beats lab scores for SEO ranking signals.
I screenshot Lighthouse before and after every performance PR. Marketing teams reference those numbers in stakeholder emails — proof beats opinions when someone asks why we delayed a feature for image optimisation.
The single takeaway — measure, checklist, ship
A web performance checklist only works if it blocks bad deploys. Paste the twenty rows into every release PR, attach Lighthouse PDFs, and link to field data after launch. When LCP regresses two weeks post-deploy, you will know which change caused it because you measured before. Performance is kindness to users on ₹15,000 Android phones — the majority of traffic for many India-first products.
// AFTER — PR template snippet (paste into description)
## Performance gate
- [ ] Lighthouse mobile LCP < 2.5s (link)
- [ ] CLS < 0.1 (link)
- [ ] Bundle budget OK (analyzer screenshot)
- [ ] Tested 4G throttle + mid Android deviceDeep dive with measured diffs: How I cut load time by 60% with Next.js App Router. Portfolio examples: safdarali.in/projects.
Accessibility overlaps performance more than teams admit: missing alt text does not hurt LCP, but keyboard traps from hydration-heavy modals hurt INP and user trust. Run axe in the same PR as Lighthouse. Fix focus management in client islands before adding animation libraries — Framer Motion is excellent when imported only where motion matters, not on the root layout.
For Indian e-commerce and fintech, regulators and users both care about perceived speed. Show skeletons that match final layout dimensions — skeletons wrong by twenty pixels cause CLS when data loads. Prefer server-rendered price and stock on first paint; client-only price fetch is a conversion killer on flaky networks between Bengaluru offices and tier-2 cities.
When stakeholders push a heavy analytics script, negotiate load order: after onload, or behind consent. Document the INP regression in the ticket — numbers change minds faster than opinions. My checklist is political because performance is cross-functional; engineering owns the fix, product owns the tradeoff, marketing owns the script request.
Tooling I keep open during checks: Chrome Lighthouse, WebPageTest filmstrip, Next.js bundle analyzer, and Vercel Speed Insights for field data. For local dev, disable React Strict Mode double-render only when profiling — not in production builds. Compare staging URL to production URL on the same checklist; staging without CDN is not a valid sign-off for LCP. Document environment in the PR: "Lighthouse on staging with Mumbai WebPageTest, 4G throttled, Moto G4 emulation."
Regression prevention: add a CI step that fails when first-load JS exceeds budget.json thresholds. Even a coarse budget — 200 KB gzip per marketing route — catches accidental icon pack imports. Pair with eslint-plugin-no-barrel-files or similar to stop tree-shaking killers. Performance is a feature you defend in CI, not a one-time launch heroics story.
Copy this checklist into Notion, Linear, or GitHub issue templates — version it when Core Web Vitals thresholds change. A living checklist beats a PDF from 2023 that still mentions FID as the primary interaction metric.
Ship when the checklist is green — not when the demo looks fine on your MacBook Pro on office Wi-Fi. Your users in Indore and Kochi do not have that laptop. Measure where users are, then ship today.
If this helped you
I publish free tutorials and write-ups like this in my spare time — no paywall on the guides. If it saved you an afternoon of trial and error, you can support the work:
- Buy me a coffee at buymeacoffee.com/safdarali
- Subscribe to my YouTube channel — it's free; 70+ React & Next.js tutorials
Related reading
More guides on safdarali.in — same author, production-focused.
- FrameSnap
Free Video Thumbnail Generator Online — No Upload
Free video thumbnail generator online — extract frames from video without upload. Browser-based, no watermark, YouTube thumbnail from MP4 workflow.
Jun 2026Read article →
- Performance
Why Your Next.js 15 App is Still Slow (And How to Fix the React 19 Hydration Lag)
Next.js 15 performance optimization — fix INP, LCP layout shifts, React 19 hydration errors, and React Compiler gaps with a production DevTools workflow.
May 2026Read article →