Introduction đ
Images and fonts are often the heaviest assets on any web page, making them a critical target for optimization. Next.js ships with built-in Image and next/font primitives that handle resizing, lazy loading, format conversion, and font loading automatically. This tutorial covers both in depth.
Information
Image Optimization đ¯
Unoptimized images are one of the most common causes of slow page loads. Next.js automatically resizes, compresses, and serves images in modern formats like WebP, tailored to each user's device.
- Automatic resizing to the exact dimensions needed.
- Serving modern formats like WebP or AVIF when supported.
- Built-in lazy loading for images below the fold.
- Preventing layout shift by reserving space ahead of time.
The Image Component đ§Š
The <Image> component from next/image extends the HTML <img> element with automatic optimization. It requires a width and height (or fill) to prevent layout shift.
components/Hero.tsx
import Image from "next/image";
export default function Hero() {
return <Image src="/hero.jpg" alt="Team collaborating" width={1200} height={600} />;
}Local Images đ
Local images imported directly from your project automatically receive their width and height from the file itself, so you don't need to specify them manually.
components/Logo.tsx
import Image from "next/image";
import logo from "@/public/logo.png";
export default function Logo() {
return <Image src={logo} alt="Company logo" />;
}Tip
Remote Images đ
For remote images â those hosted on an external domain â you must explicitly provide width and height, and allowlist the domain in your Next.js config.
components/Avatar.tsx
import Image from "next/image";
export default function Avatar({ url }: { url: string }) {
return <Image src={url} alt="User avatar" width={64} height={64} />;
}next.config.ts
const nextConfig = {
images: {
remotePatterns: [{ protocol: "https", hostname: "images.example.com" }],
},
};
export default nextConfig;Important
Responsive Images đ
The sizes prop tells the browser how much screen space an image will occupy at different viewport widths, letting Next.js serve an appropriately sized version instead of a one-size-fits-all image.
components/Banner.tsx
import Image from "next/image";
export default function Banner() {
return (
<Image
src="/banner.jpg"
alt="Promotional banner"
fill
sizes="(max-width: 768px) 100vw, 50vw"
/>
);
}Image Sizing đ
Explicit width and height values define the image's aspect ratio, which the browser uses to reserve layout space before the image finishes loading â preventing content from jumping around.
components/ProductPhoto.tsx
<Image src="/product.jpg" alt="Product photo" width={400} height={400} />Image Quality đī¸
The quality prop (1â100) controls the compression level of the optimized image, trading off visual fidelity against file size. The default is 75, which works well for most photos.
components/HeroImage.tsx
<Image src="/hero.jpg" alt="Hero" width={1200} height={600} quality={90} />Image Formats đī¸
Next.js automatically negotiates the best supported format per browser â serving AVIF or WebP to modern browsers while falling back to the original format for older ones.
| Format | Typical Use |
|---|---|
| AVIF | smallest file size, newest browsers |
| WebP | broad modern browser support |
| Original (JPEG/PNG) | fallback for unsupported browsers |
Blur Placeholders đĢī¸
Setting placeholder="blur" shows a low-resolution, blurred version of the image while the full image loads, providing a smoother visual transition instead of a blank space.
components/ArticleImage.tsx
import Image from "next/image";
import photo from "@/public/photo.jpg";
export default function ArticleImage() {
return <Image src={photo} alt="Article header" placeholder="blur" />;
}Note
Lazy Loading Images đ¤
By default, every <Image> is lazy loaded â it only starts fetching once it's about to enter the viewport, saving bandwidth on images the user may never scroll to.
components/Gallery.tsx
<Image src="/gallery-1.jpg" alt="Gallery photo" width={300} height={300} />Priority Images đ
For an image visible above the fold on initial load â like a hero banner â set the priority prop to disable lazy loading and preload it, improving Largest Contentful Paint.
components/HeroBanner.tsx
<Image src="/hero.jpg" alt="Hero banner" width={1200} height={600} priority />Best Practice
Fill Images đŧī¸
The fill prop makes an image expand to fill its parent container, which must be positioned relatively â useful for responsive layouts where exact pixel dimensions aren't known ahead of time.
components/CardImage.tsx
<div style={{ position: "relative", width: "100%", height: 300 }}>
<Image src="/card.jpg" alt="Card image" fill style={{ objectFit: "cover" }} />
</div>Image Styling đ¨
Images accept normal className and style props, with objectFit and objectPosition being especially common for controlling how an image crops within its box.
components/Thumbnail.tsx
<Image
src="/thumb.jpg"
alt="Thumbnail"
fill
style={{ objectFit: "cover", borderRadius: 8 }}
/>Image Security đ
Because remote images must be explicitly allowlisted via remotePatterns, Next.js prevents your app from being used as an open image proxy for arbitrary, untrusted URLs.
Caution
Image Configuration âī¸
The images key in next.config.ts controls global image behavior â allowed domains, supported formats, device sizes, and cache duration for optimized images.
next.config.ts
const nextConfig = {
images: {
formats: ["image/avif", "image/webp"],
deviceSizes: [640, 750, 828, 1080, 1200],
minimumCacheTTL: 60,
},
};
export default nextConfig;Common Image Patterns đēī¸
components/Hero.tsx
<Image src="/hero.jpg" alt="Hero" fill priority style={{ objectFit: "cover" }} />components/Avatar.tsx
<Image src={user.avatarUrl} alt={user.name} width={40} height={40} style={{ borderRadius: "50%" }} />components/Gallery.tsx
{photos.map((photo) => (
<Image key={photo.id} src={photo.url} alt={photo.alt} width={200} height={200} />
))}Font Optimization đ¤
Fonts can significantly impact loading performance and cause visible layout shift if not handled carefully. The next/font package self-hosts fonts and eliminates extra network requests to third-party font services.
The next/font Package đĻ
next/font automatically downloads and self-hosts font files at build time â including Google Fonts â so no requests are sent to external servers at runtime, improving both privacy and performance.
app/layout.tsx
import { Inter } from "next/font/google";
const inter = Inter({ subsets: ["latin"] });
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={inter.className}>
<body>{children}</body>
</html>
);
}Tip
Google Fonts đĄ
Importing any font from next/font/google automatically downloads it at build time and serves it from your own domain â no <link> tag or external stylesheet needed.
app/layout.tsx
import { Roboto } from "next/font/google";
const roboto = Roboto({
weight: ["400", "700"],
subsets: ["latin"],
});Local Fonts đž
For custom or licensed fonts, next/font/local optimizes and self-hosts font files that already live in your project, applying the same layout-shift prevention as Google Fonts.
app/layout.tsx
import localFont from "next/font/local";
const myFont = localFont({
src: "./fonts/CustomFont.woff2",
display: "swap",
});Variable Fonts đī¸
Variable fonts pack multiple weights and styles into a single file, and next/font automatically uses the variable version of a Google Font when one is available, reducing the number of downloaded files.
app/layout.tsx
import { Inter } from "next/font/google";
const inter = Inter({
subsets: ["latin"],
variable: "--font-inter",
});Font Display đī¸
The display option controls how a font renders while it's still loading â "swap" shows fallback text immediately and swaps in the custom font once ready, avoiding invisible text.
app/layout.tsx
import { Inter } from "next/font/google";
const inter = Inter({ subsets: ["latin"], display: "swap" });| Value | Behavior |
|---|---|
| swap | show fallback immediately, swap when ready |
| block | briefly hide text, then swap |
| optional | use custom font only if it loads very quickly |
Font Subsetting âī¸
The subsets option tells next/font to include only the character sets your app actually needs â like "latin" â dramatically reducing font file size.
app/layout.tsx
import { Noto_Sans } from "next/font/google";
const notoSans = Noto_Sans({ subsets: ["latin", "cyrillic"] });Multiple Fonts đ
You can load multiple fonts side by side â for example, a heading font and a body font â each assigned to its own CSS variable for use throughout your stylesheets.
app/layout.tsx
import { Inter, Playfair_Display } from "next/font/google";
const inter = Inter({ subsets: ["latin"], variable: "--font-body" });
const playfair = Playfair_Display({ subsets: ["latin"], variable: "--font-heading" });
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={`${inter.variable} ${playfair.variable}`}>
<body>{children}</body>
</html>
);
}Font Performance đ
- Self-hosting via next/font removes external network requests entirely.
- Font files are automatically included in the preload headers for faster rendering.
- No layout shift occurs, since fallback font metrics are automatically matched.
Font Best Practices â
- Load fonts once in the root layout rather than per-page to avoid duplication.
- Only include the subsets your content actually uses.
- Prefer variable fonts when available to reduce the number of font files.
- Use display: "swap" unless you have a specific reason not to.
Accessibility Considerations âŋ
Every <Image> requires meaningful alt text describing its content for assistive technology users; decorative images should use an empty alt="" instead.
components/DecorativeIcon.tsx
<Image src="/decoration.svg" alt="" width={24} height={24} />Performance Optimization đ
- Reserve exactly one priority image per page â typically the largest above-the-fold image.
- Always specify accurate sizes for responsive images to avoid over-fetching.
- Combine next/font with variable fonts to minimize total font payload.
- Avoid loading more font weights or styles than your design actually uses.
Common Mistakes â ī¸
- Forgetting width/height or fill, causing layout shift or a console warning.
- Not allowlisting a remote image domain in next.config.ts.
- Marking every image as priority, which negates its performance benefit.
- Using a traditional Google Fonts <link> tag instead of next/font/google.