Skip to main content
Chapter 7 Deployment and Performance

Image and Font Optimization

18 min read Lesson 26 / 28

Automatic Image and Font Optimization

Next.js provides built-in components that automatically optimize images and fonts — two of the biggest contributors to poor Core Web Vitals scores.

The Image Component

import Image from 'next/image';

// Local image — size is known at build time
import heroImage from '@/public/hero.jpg';

export function Hero() {
    return (
        <Image
            src={heroImage}
            alt="Hero image"
            priority           // Above the fold: preload this image
            placeholder="blur" // Shows blurred placeholder while loading
            className="w-full rounded-xl"
        />
    );
}

// Remote image — must specify width and height
export function Avatar({ user }: { user: { name: string; image: string } }) {
    return (
        <Image
            src={user.image}
            alt={user.name}
            width={48}
            height={48}
            className="rounded-full"
        />
    );
}

// Fill mode for unknown dimensions (use in a positioned container)
export function CoverImage({ src, alt }: { src: string; alt: string }) {
    return (
        <div className="relative aspect-video">
            <Image
                src={src}
                alt={alt}
                fill
                sizes="(max-width: 768px) 100vw, 50vw"
                className="object-cover rounded-lg"
            />
        </div>
    );
}

The sizes prop is critical for remote images — it tells the browser which size to request based on viewport width, preventing downloading a 2000px image for a 400px slot.

Font Optimization with next/font

// app/layout.tsx
import { Inter, Playfair_Display, JetBrains_Mono } from 'next/font/google';

const inter = Inter({
    subsets: ['latin'],
    variable: '--font-inter',
    display: 'swap',
});

const playfair = Playfair_Display({
    subsets: ['latin'],
    variable: '--font-playfair',
    weight: ['400', '700'],
    display: 'swap',
});

const mono = JetBrains_Mono({
    subsets: ['latin'],
    variable: '--font-mono',
    display: 'swap',
});

export default function RootLayout({ children }) {
    return (
        <html lang="en" className={`${inter.variable} ${playfair.variable} ${mono.variable}`}>
            <body className="font-sans">{children}</body>
        </html>
    );
}
/* tailwind.config.ts */
theme: {
    extend: {
        fontFamily: {
            sans: ['var(--font-inter)'],
            serif: ['var(--font-playfair)'],
            mono: ['var(--font-mono)'],
        },
    },
},

next/font downloads fonts at build time, self-hosts them, and injects size-adjust CSS to eliminate layout shift. No external font requests in the browser.