🚀 SEO, Performance & Deploy · Lesson 29
Performance & Core Web Vitals
Core Web Vitals in simple words
| Metric | Question it answers | Good | Main Next.js fix |
|---|---|---|---|
| LCP – Largest Contentful Paint | How fast does the main content appear? | < 2.5 s | Static pages, priority hero image |
| INP – Interaction to Next Paint | How fast does the page react to a tap? | < 200 ms | Less client JS, small Client Components |
| CLS – Cumulative Layout Shift | Does content jump while loading? | < 0.1 | width/height on images, next/font |
How to measure
- 1npm run build && npm run start (never measure dev mode)
- 2Chrome DevTools → Lighthouse → Mobile → Analyze
- 3pagespeed.web.dev for the live URL (real-user data after some traffic)
- 4Search Console → Core Web Vitals report
1. Prefer static rendering
A static (○/●) page is served from cache in milliseconds. Avoid cookies()/headers() in layouts; use ISR (revalidate) instead of SSR for content that changes occasionally.
2. Images and fonts
<Image>everywhere, correctsizes,priorityonly on the hero.next/fontwithdisplay: 'swap'; 1–2 font families, few weights.- Compress source images (EduSite course images are 1200×675 PNG; Next.js serves WebP).
3. Send less JavaScript
JSX – keep client islands small
// ❌ whole page becomes client JS
'use client'
export default function CoursePage() { /* big page + one Like button */ }
// ✅ page stays on the server, only the button ships JS
export default function CoursePage() {
return (<>
<BigServerContent />
<LikeButton /> {/* 'use client' inside this small file */}
</>)
}Terminal – bundle analyzer
# See which packages make your JavaScript big (Turbopack bundle analyzer) npx next experimental-analyze # Then open the printed local URL and look for large modules
4. Lazy-load heavy components
JSX – next/dynamic
// app/courses/[slug]/page.jsx
import dynamic from 'next/dynamic'
// The chart library (≈ 200 KB) loads only when this component is rendered
const FeeChart = dynamic(() => import('@/components/FeeChart'), {
loading: () => <p>Loading chart…</p>,
})
// In a Client Component you can also skip server rendering:
// const Map = dynamic(() => import('./Map'), { ssr: false })5. Third-party scripts with next/script
JSX – next/script strategies
// app/layout.jsx
import Script from 'next/script'
<Script src="https://www.googletagmanager.com/gtag/js?id=G-XXXX" strategy="afterInteractive" />
<Script id="ga" strategy="afterInteractive">
{`window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date()); gtag('config', 'G-XXXX');`}
</Script>
// Chat widget that is not urgent:
<Script src="https://widget.example.com/chat.js" strategy="lazyOnload" />beforeInteractive
- Blocks the page
- Only for critical scripts (consent)
- Rarely needed
afterInteractive / lazyOnload
- Loads after the page is usable
- Analytics, ads, chat
- Protects LCP and INP
6. Fast data
Promise.allfor independent queries; database indexes on columns in WHERE.- Cache with
revalidate/ tags; stream slow parts with<Suspense>. - Host the server close to your users (Mumbai region for India).
Before-launch checklist
- 1Lighthouse mobile: Performance ≥ 90, SEO 100, Accessibility ≥ 95
- 2No console errors; no 404 for images/fonts
- 3All images via next/image with alt text
- 4Analytics via next/script afterInteractive
- 5Build output: content pages ○/● not ƒ

Common mistakes
- Measuring in dev mode → numbers are meaningless.
- Huge libraries for small jobs (moment.js for one date) → use
Intl.DateTimeFormat. - Auto-playing hero videos on mobile → kills LCP; use a poster image.
Summary
- LCP, INP, CLS are the three numbers Google uses.
- Static rendering + next/image + next/font solve most problems.
- Small client islands,
next/dynamic,next/scriptkeep JavaScript low.
✅ Practice
Run Lighthouse (mobile) on your production build, write down the three metrics, apply two fixes from this lesson and measure again.
Frequently Asked Questions
Why is my Lighthouse score low in npm run dev?
Development mode is not optimized. Always test with npm run build and npm run start, or on the deployed site.
What is a good LCP?
Under 2.5 seconds on mobile is good. Next.js static pages with an optimized hero image usually reach under 1.5 seconds.
Does Google Analytics slow down Next.js?
It can if loaded early. Load it with next/script strategy="afterInteractive" or lazyOnload so it does not block the page.
