Crash Courses · Masters
Image, Font & Metadata Optimization
Optimize Core Web Vitals with next/image, next/font, and the Metadata API.
10 min read+175 XP on completionCert: Next.js Crash Course
Tap any word in the text below to start reading from there.
Image, Font & Metadata Optimization
Three of the biggest Core Web Vitals killers are images, fonts, and missing metadata. Next.js has built-in solutions for all three.
next/image — Automatic Optimization
import Image from 'next/image'
// Fixed-size — avatar, logo, icon
<Image
src="/logo.png"
alt="JST Academy"
width={120}
height={40}
priority // LCP image — skip lazy loading, load immediately
/>
// Responsive — hero, card cover
<div className="relative h-64">
<Image
src="/hero.jpg"
alt="Academy Hero"
fill // fills the parent container
className="object-cover"
sizes="(max-width: 768px) 100vw, 50vw" // hints for responsive sizing
priority
/>
</div>What next/image does automatically:
- Converts images to WebP or AVIF (smaller file sizes)
- Applies lazy loading unless
priorityis set - Reserves the correct space before the image loads (prevents CLS)
- Generates a blur placeholder for progressive loading (with
placeholder="blur") - Generates responsive sizes via
srcsetfor thesizesprop
For fill images, the parent must have position: relative.
next/font — Zero-Shift Fonts
// src/app/layout.tsx
import { Inter, Playfair_Display } from 'next/font/google'
const inter = Inter({
subsets: ['latin'],
variable: '--font-inter', // CSS variable for Tailwind
})
const playfair = Playfair_Display({
subsets: ['latin'],
variable: '--font-playfair',
weight: ['400', '700'],
})
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={`${inter.variable} ${playfair.variable}`}>
<body className={inter.className}>{children}</body>
</html>
)
}next/font downloads fonts at build time and hosts them on your domain. No external Google Fonts DNS request at runtime, no layout shift when fonts swap in, and no user tracking by Google (GDPR-friendly).
Metadata API — SEO and Social Tags
// app/layout.tsx — static metadata (applies to all pages via template)
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: {
template: '%s | JST Academy', // page titles become "React Course | JST Academy"
default: 'JST Academy',
},
description: 'PhD-level crash courses for modern web developers.',
openGraph: {
images: [{ url: '/og-image.jpg', width: 1200, height: 630 }],
siteName: 'JST Academy',
},
twitter: {
card: 'summary_large_image',
},
}// app/courses/[id]/page.tsx — dynamic metadata from route params
export async function generateMetadata({
params
}: {
params: Promise<{ id: string }>
}): Promise<Metadata> {
const { id } = await params
const course = await getCourse(id)
if (!course) return { title: 'Not Found' }
return {
title: course.title, // becomes "React Crash Course | JST Academy"
description: course.moduleObjective,
openGraph: {
title: course.title,
description: course.moduleObjective,
images: [{ url: `/og/${id}.jpg`, width: 1200, height: 630 }],
},
}
}Verifying Your Optimization
Test your pages with:
- Google PageSpeed Insights — real-world Core Web Vitals scores
- Chrome DevTools Network tab — verify images serve WebP format
- Twitter Card Validator / Facebook Debugger — verify OG tags render correctly on social
Reading progress
0% read
In this module
next/image
next/font
Metadata API
sizes prop
0%