StarterApp Docs
Performance

Images & Fonts

Automatic optimization for faster visual loading

Pro Tip

Use next/image and next/font for automatic optimization. These components handle format selection, lazy loading, and layout stability without manual configuration.

Image Optimization

Images account for 50-70% of page weight. The Next.js Image component automatically optimizes format, size, and loading behavior.

The Next.js Image component handles these concerns automatically:

Format Optimization

Serves WebP or AVIF to modern browsers with automatic fallbacks

Responsive Sizing

Generates multiple resolutions and serves the appropriate size per device

Lazy Loading

Defers off-screen images until users scroll, reducing initial payload

Layout Stability

Reserves space to prevent cumulative layout shift (CLS)

Implementing Next.js Image

Replace standard <img> tags with the Next.js Image component:

import Image from 'next/image';

export function ProductCard({ product }) {
  return (
    <Image
      src={product.imageUrl}
      alt={product.name}
      width={400}
      height={300}
      className="rounded-lg"
    />
  );
}

This automatically:

  • Generates multiple sizes (1x, 2x)
  • Converts to WebP/AVIF
  • Lazy loads when off-screen
  • Prevents layout shift
export function ProductCard({ product }) {
  return (
    <img
      src={product.imageUrl}
      alt={product.name}
      className="rounded-lg"
    />
  );
}

Standard HTML images:

  • Load original format and size
  • Block parsing until loaded
  • Cause layout shifts
  • Download immediately regardless of viewport position

Always specify dimensions

Width and height attributes prevent layout shifts. Next.js uses these values to reserve space before the image loads.

Responsive Images with Sizes

The sizes prop tells Next.js which image resolution to generate for different viewports:

<Image
  src="/hero-banner.jpg"
  alt="Product showcase"
  fill
  sizes="(max-width: 768px) 100vw,
         (max-width: 1200px) 50vw,
         33vw"
  className="object-cover"
/>

This configuration generates:

  • Mobile (≤768px): Full viewport width
  • Tablet (≤1200px): 50% viewport width
  • Desktop (>1200px): 33% viewport width

Next.js generates appropriately sized images for each breakpoint. Mobile users download smaller files, reducing bandwidth consumption and improving load times.

Use fill for unknown dimensions

When image dimensions aren't known at build time (user uploads, CMS content), use the fill prop with a positioned parent container.

Remote Image Patterns

Applications often load images from external sources. The framework requires explicit configuration for security:

images: {
  remotePatterns: [
    { protocol: "https", hostname: "lh3.googleusercontent.com" },
    { protocol: "https", hostname: "avatars.githubusercontent.com" },
    { protocol: "https", hostname: "secure.gravatar.com" },
  ],
}

This configuration exists in both apps. It allows optimization for:

Security consideration

Only add trusted domains. Remote patterns allow Next.js to proxy and cache images from these sources. Untrusted domains could consume server resources.

Priority Loading for Above-Fold Content

Critical images should load immediately rather than waiting for lazy load thresholds:

<Image
  src="/hero-banner.jpg"
  alt="Welcome"
  width={1200}
  height={600}
  priority
/>

The priority prop:

  • Preloads the image in <head>
  • Disables lazy loading
  • Ensures above-fold images appear instantly
  • Should only apply to content visible on initial page load

Limit priority images

Only use priority for 1-2 images per page. Excessive preloading defeats the purpose of optimization.

Font Optimization

Web fonts cause layout shifts when they load after content renders. Next.js font optimization eliminates this problem through automatic font subsetting and preloading.

Google Fonts Integration

The framework includes built-in Google Fonts support:

import { Inter, Roboto_Mono } from 'next/font/google';

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

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

export default function RootLayout({ children }) {
  return (
    <html className={`${inter.variable} ${robotoMono.variable}`}>
      <body>{children}</body>
    </html>
  );
}

This configuration:

Downloads fonts at build time

Next.js fetches font files during build and self-hosts them. No runtime requests to Google Fonts CDN.

Subsets the font files

Only includes characters for specified subsets (latin, cyrillic, etc.), reducing file size.

Preloads critical fonts

Adds <link rel="preload"> for instant availability. Prevents flash of unstyled text (FOUT).

Applies CSS variables

Use variables in Tailwind config: fontFamily: { sans: ['var(--font-inter)'] }

Local Font Files

For custom fonts, use the localFont helper:

import localFont from 'next/font/local';

const customFont = localFont({
  src: [
    {
      path: './fonts/custom-regular.woff2',
      weight: '400',
      style: 'normal',
    },
    {
      path: './fonts/custom-bold.woff2',
      weight: '700',
      style: 'normal',
    },
  ],
  variable: '--font-custom',
});

Font Display Strategies

The display property controls font loading behavior:

Font display strategies:

StrategyBehaviorUse Case
swapShow fallback immediately, swap when font loadsBest for most cases - prevents invisible text
optionalUse fallback if font doesn't load quicklyPrevents layout shifts, may not load custom font
blockHide text until font loads (max 3s)Use sparingly - creates invisible text
fallbackBrief invisible period, then show fallbackBalanced between swap and block

Avoid font-display: block

Blocking text renders creates poor user experience. Users see blank spaces where content should appear. Use swap or optional instead.

Measuring Asset Performance

Track asset optimization impact through Core Web Vitals:

Next Steps