Images are usually the heaviest assets on a page, and web fonts are a classic cause of layout jank. Next.js ships dedicated tools for both: the Image component and the next/font module. Used correctly, they noticeably improve Core Web Vitals - which affects both user experience and search ranking.
A plain img tag ships the original file at its original size to every device, can shift the layout when it loads, and loads even when it is far below the fold. The Next.js Image component fixes all three automatically.
import Image from 'next/image';
import hero from '@/public/hero.jpg';
export default function Hero() {
return (
<Image
src={hero}
alt='Developer working on a laptop'
priority
className='rounded-xl'
/>
);
}What you get automatically:
For local images imported like above, dimensions are automatic. For remote images you must supply width and height yourself and allow the domain in next.config.mjs:
// next.config.mjs
const nextConfig = {
images: {
remotePatterns: [
{ protocol: 'https', hostname: 'images.unsplash.com' },
],
},
};
export default nextConfig;When an image should stretch to its container - a card thumbnail, a banner - use fill with a positioned parent:
<div style={{ position: 'relative', height: 300 }}>
<Image src='/banner.jpg' alt='' fill style={{ objectFit: 'cover' }}
sizes='(max-width: 768px) 100vw, 50vw' />
</div>The sizes attribute tells the browser how wide the image will render so it downloads the smallest sufficient file. Two more tips: add priority to the largest above-the-fold image (usually your hero) so it is not lazy-loaded, and always write meaningful alt text for accessibility and SEO.
Loading Google Fonts from a CDN link adds an extra network round-trip and can flash unstyled text. next/font downloads the font at build time and serves it from your own domain - no external request, no layout shift, and better privacy:
// app/layout.js
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'], display: 'swap' });
export default function RootLayout({ children }) {
return (
<html lang='en' className={inter.className}>
<body>{children}</body>
</html>
);
}The font is self-hosted automatically and a matching fallback is size-adjusted to minimize shift while it loads. For custom font files, use next/font/local:
import localFont from 'next/font/local';
const brand = localFont({ src: './brand.woff2' });You can also expose fonts as CSS variables (variable: '--font-inter') and reference them from Tailwind or CSS Modules.
Run a Lighthouse audit before and after adopting these components on an image-heavy page. Largest Contentful Paint and Cumulative Layout Shift typically improve immediately - these are the metrics Google uses in ranking, which is why this lesson matters for more than aesthetics.
Next up: Lesson 12 goes deeper into search visibility with the Metadata API and SEO best practices.