What you'll learn
By the end of this you'll know which Lighthouse metrics actually matter for a content site, the first fixes to try for each one, and the order to apply them so you're not optimizing hero animations while a 400KB client bundle loads on every article page.
This cheat sheet is tuned for Next.js App Router and editorial pages — blogs, docs, resource hubs — not single-page apps where the entire product lives behind one hydration boundary.
Who this is for
- Frontend developers responsible for Core Web Vitals on a content-heavy site
- Full-stack builders using Next.js who got a Lighthouse report and want a prioritized fix list
- Anyone who was told "make it green" without a clear definition of what to change first
Skip this if you're building a dashboard where heavy client interactivity is the product. Different tradeoffs, different budgets — this sheet assumes reading is the primary job.
What is Lighthouse telling you?
Plain English: Lighthouse is a lab test that approximates how fast and stable your page feels on a mid-tier mobile device with a throttled connection.
It is not Google ranking you directly from your laptop score. It is a consistent ruler — same throttling, same audits — so you can compare before and after. Treat it as a diagnostic, not a trophy. A 100 score with fake content isn't the goal; a fast real page with real fonts and real images is.
For content sites, three metrics cause most of the pain: LCP (does the main content show up?), CLS (does stuff jump around?), INP (does the page respond when tapped?). Everything else supports those three.
Prerequisites
- A production or production-like build (
next build+next start, not dev mode) - Chrome DevTools Lighthouse tab or PageSpeed Insights on a public URL
- One representative slow page — usually homepage or a long article with images
- Access to change
next/image, fonts, and layout components
Setup from zero
Run Lighthouse correctly once before you change anything. Most "we tried everything" stories start with measuring dev mode or a page that isn't what users actually hit.
Step 1 — Pick the canonical test page
Choose the URL that matters most: homepage, top traffic article, or template that every post uses. Fix the template, fix the fleet.
Step 2 — Baseline in production mode
npm run build && npm run start
Run Lighthouse mobile, clear storage, single page load. Save the JSON or screenshot. Circle LCP element, CLS offenders, and total JavaScript in the report.
Step 3 — Identify the LCP element
In the Lighthouse trace, find what painted last for LCP — usually hero image, hero text, or a web font blocking text. Your first optimization targets that node, not random components below the fold.
Little tip: if LCP is text but Web Font Load delays it, fix fonts before compressing images. Chasing the wrong LCP element wastes an afternoon.
Step 4 — Set a JS budget per route type
Article pages: aim for minimal client JS — nav, analytics if you must, one interactive widget max. Tool pages: higher budget, but isolate heavy code to /tools/* routes. Write the budget down (e.g. "article < 100KB transferred JS").
Step 5 — Re-run after each change
One change, one measurement. Bundling five fixes makes you guess which one helped.
The mental model
The mental model for content-site performance is the server sends HTML; the browser should read it.
Every kilobyte of JavaScript you add is a bet that interactivity is worth delaying parse and execution. On an article page, the bet usually loses. Server Components and static HTML are the default; client islands are the exception you justify.
LCP rewards getting meaningful pixels on screen early. CLS rewards reserving space before assets arrive. INP rewards not blocking the main thread with hydration and long tasks. All three push the same direction: less client work on the critical path.
Key terms
LCP (Largest Contentful Paint) — when the largest visible content element renders. Usually hero image or main heading block.
CLS (Cumulative Layout Shift) — visual stability score; penalizes elements that move after first paint.
INP (Interaction to Next Paint) — responsiveness to user input; replaces FID as the interaction metric to watch.
Critical path — the sequence of network and CPU work required before the user sees and can use primary content.
Client island — a small "use client" component hydrated inside an otherwise server-rendered page.
Priority hint — priority on next/image for the one LCP image per page.
Step-by-step: fixes by metric
LCP — get the main thing visible fast
- Server-render hero text and lead image — no client-only wrappers hiding content until hydration.
- One
priorityimage — the LCP candidate getspriority; everything else lazy-loads. - Explicit width and height — on every
next/imageand meaningful<img>. - Font strategy —
next/fontwithdisplay: swapor optional subset; avoid invisible text during load. - No opacity-0 entrance animations on LCP nodes — fade-ins delay the metric; animate something else.
import Image from "next/image";
<Image
src={hero.src}
alt={hero.alt}
width={1200}
height={630}
priority
sizes="(max-width: 768px) 100vw, 720px"
/>
CLS — stop the page from jumping
- Reserve space for embeds, ads, and tool slots — min-height on containers even before content loads.
- Never inject banners above existing content without reserved height — classic CLS killer.
- Font fallbacks with similar metrics — reduce reflow when web fonts swap in.
- Aspect-ratio boxes for images and video — CSS
aspect-ratioor explicit dimensions. - Avoid inserting DOM above the fold after load — cookie banners, A/B widgets, lazy nav.
INP — keep the main thread available
- Minimize client components on article templates — navigation and theme toggle, not the whole article shell.
- Defer non-critical third-party scripts — if analytics isn't green yet, it waits (per project standards).
- Split heavy widgets to separate routes — calculators and demos get their own page and bundle.
- Virtualize long lists — archive pages with hundreds of cards need list virtualization, not 500 hydrated cards.
- Measure long tasks in Performance panel — find the 200ms+ blocks and remove or defer them.
Little tip: run Lighthouse with "CPU throttling 4x" and tap the mobile nav during the trace. INP problems often hide until you interact — a page that looks fast on passive load can still feel sticky.
Working examples
Article layout: server by default
// app/resources/[slug]/page.tsx — server component
export default async function ResourcePage({ params }) {
const post = await getContentBySlug(params.slug);
return (
<article>
<h1>{post.title}</h1>
<Prose html={post.body} /> {/* server-rendered HTML */}
</article>
);
}
Reserve space for a deferred tool embed
.tool-slot {
min-height: 420px;
contain: layout style;
}
Patterns / when to use each technique
| Technique | Use when |
|-----------|----------|
| priority image | One LCP candidate per page — hero or lead image |
| loading="lazy" | Below-fold images and avatars |
| next/font | Any custom brand font on content pages |
| Route-level code split | Heavy interactive tools, not article chrome |
| Static generation / ISR | Published content with predictable traffic |
Common mistakes
Optimizing dev mode scores — Turbopack dev is not production. Always measure next build.
Multiple priority images — browsers compete; pick one LCP candidate.
Client-fetching article body — hurts LCP and SEO; server-render primary content.
Huge icon packs imported globally — import one SVG or use a subset font.
Chasing 100 on desktop while mobile fails — mobile throttling is the constraint that matches most real users.
Troubleshooting
LCP stuck above 2.5s after image optimization — check TTFB (slow server/data fetch) and font blocking. Run WebPageTest for waterfall clarity.
CLS only in production — compare ads, cookie banners, and fonts loaded only in prod. Staging without those embeds lies to you.
Good lab score, bad field data — real users have slower devices and cache states. Check Search Console Core Web Vitals report for URL groups.
INP bad on one component — wrap heavy work in startTransition, move work off the click handler, or shrink the client boundary.
Checklist
- [ ] Baseline Lighthouse saved from production build on representative URL
- [ ] LCP element identified; one
priorityimage or font fix applied - [ ] All content images have width/height or aspect-ratio
- [ ] Article template is server-rendered; client JS budget documented
- [ ] Embed/ad slots reserve min-height
- [ ] Re-measured after each change; regression noted in PR
Practice task
Take your slowest article URL. Run Lighthouse mobile on production build. Fix only LCP this week — one change at a time until LCP element paints under 2.5s lab or you've documented a server-side blocker. Next week CLS, then INP. Serial focus beats five simultaneous optimizations.
FAQ
What score should we target?
Green metrics matter more than the total score. Aim for LCP ≤ 2.5s, CLS ≤ 0.1, INP ≤ 200ms at p75 field data when available.
Does Server Components always fix it?
It removes unnecessary client JS — huge for content — but slow data fetching or unoptimized images still hurt. Server render is necessary, not sufficient.
Are animations forbidden?
No. Animate non-LCP elements. Respect prefers-reduced-motion. Don't hide the headline until an animation completes.
Should every page be static?
Static or ISR for published content is ideal. Personalized pages need dynamic rendering — optimize the shared shell and cache what you can.
What to learn next
- Next.js metadata and SEO — performance and SEO share the same server-render defaults
- SEO content brief template — brief writers on image dimensions and heading structure upfront
- Building the Baseline content hub — architecture choices that keep article pages fast at scale
Related on Baseline
- [Next.js metadata and SEO](/developers/nextjs/nextjs-metadata-and-seo)
- [SEO content brief template](/resources/seo-content-brief-template)
- [Building the Baseline content hub](/case-studies/building-baseline-content-hub)
Takeaways
Lighthouse on a content site is a guide to three questions: did the main content show up, did the layout stay still, and does input feel responsive. Fix LCP first on the element the report names, reserve space before assets load, and keep article routes thin on client JavaScript.
Measure production builds, change one thing at a time, and don't animate the headline away.
If you remember only one thing: on article pages, server-rendered HTML is the performance strategy — client JavaScript is debt you pay on every visit.