🎨 Styling, Images & Fonts · Lesson 17
next/image & next/font
Why images and fonts matter
Images are usually 70% of a page's weight. Fonts loaded late cause text to "jump". Both hurt Google's Core Web Vitals (LCP and CLS) — and therefore your ranking and click-through rate. Next.js gives two built-in tools: next/image and next/font.
Plain <img> + Google Fonts link
- Full 2 MB PNG on phones
- All images load at once
- Layout jumps when image arrives
- Extra request to fonts.googleapis.com
next/image + next/font
- Resized WebP/AVIF ~60 KB
- Lazy-loaded below the fold
- Space reserved → no jump
- Font self-hosted, zero shift
The <Image> component
JSX – components/CourseCard.jsx
// components/CourseCard.jsx (from the EduSite project)
import Image from 'next/image'
<Image
src={course.image} // "/images/nextjs.png" from public/
alt={course.title} // always describe the image (SEO + accessibility)
width={600}
height={338}
sizes="(max-width: 700px) 100vw, 360px"
/>width, height, sizes, priority
| Prop | Meaning |
|---|---|
| width / height | Intrinsic size (aspect ratio). Prevents layout shift. |
| sizes | How wide the image is shown at each screen size, so the browser downloads the right file. |
| priority | Preload — only for the main above-the-fold image. |
| quality | 1–100, default 75 (must be in images.qualities). |
| placeholder="blur" | Shows a blurred preview while loading. |
JSX – hero image with priority
// app/courses/[slug]/page.jsx — the big image at the top
<Image
src={course.image}
alt={course.title}
width={1200}
height={675}
priority // above the fold → load first (better LCP)
quality={90}
sizes="(max-width: 700px) 100vw, 560px"
style={{ borderRadius: 16 }}
/>JSX – static import with blur placeholder
// Static import: width/height and blur placeholder detected automatically
import heroPic from '@/public/images/hero.png'
<Image src={heroPic} alt="Students learning to code" placeholder="blur" priority />What next/image does for you

Remote images & config
JS – next.config.mjs
// next.config.mjs
const nextConfig = {
images: {
qualities: [75, 90], // Next.js 16: allowed quality values
remotePatterns: [
{ protocol: 'https', hostname: 'cdn.example.com', pathname: '/uploads/**' },
],
},
}
export default nextConfig
// now this works:
<Image src="https://cdn.example.com/uploads/shoe.jpg" alt="Shoe" width={400} height={400} />fill mode for cover images
When you do not know the size (a banner that stretches), use fill. The parent must have position: relative and a height.
JSX – fill
<div style={{ position: 'relative', height: 280 }}>
<Image src="/images/hero.png" alt="Classroom" fill
style={{ objectFit: 'cover' }} sizes="100vw" />
</div>next/font: Google fonts
JSX – app/layout.jsx
// app/layout.jsx
import { Poppins } from 'next/font/google'
const poppins = Poppins({ subsets: ['latin'], weight: ['400', '700'], display: 'swap' })
export default function RootLayout({ children }) {
return (
<html lang="en" className={poppins.className}>
<body>{children}</body>
</html>
)
}Local font files
JSX + CSS – local font
// app/layout.jsx (EduSite uses a local Inter file)
import localFont from 'next/font/local'
const inter = localFont({
src: './fonts/Inter.woff2',
variable: '--font-inter', // exposes a CSS variable
display: 'swap',
})
export default function RootLayout({ children }) {
return <html lang="en" className={inter.variable}><body>{children}</body></html>
}
/* globals.css */
body { font-family: var(--font-inter), system-ui, sans-serif; }Common mistakes
- Empty or missing
alton meaningful images → bad for SEO and screen readers. priorityon every image → nothing is a priority any more; use it on 1–2 images.- Remote image without
remotePatterns→ error "hostname is not configured". quality={90}without adding 90 toimages.qualitiesin Next.js 16 → error.fillwithout a positioned parent → image covers the whole page.
Summary
<Image>resizes, converts to WebP/AVIF, lazy-loads and prevents layout shift.- Give width/height (or fill), a good
alt, correctsizes;priorityfor the hero. next/fontself-hosts Google or local fonts with zero layout shift.
✅ Practice
Replace every <img> in your project with <Image>, add a Google font with next/font, then run Lighthouse in Chrome DevTools and compare the Performance score.
Frequently Asked Questions
Why does next/image require width and height?
They let the browser reserve the right space before the image loads, which prevents layout shift (CLS). With fill mode, the parent box provides the size instead.
When should I use priority?
Only for the main image visible above the fold, usually the hero or product photo. It preloads the image and improves Largest Contentful Paint (LCP).
Does next/font contact Google at runtime?
No. Fonts are downloaded at build time and served from your own domain, which is faster and better for privacy.
What is images.qualities in Next.js 16?
Next.js 16 only allows the quality values listed in images.qualities (default [75]). If you use quality={90}, add 90 to that list in next.config.
