Next.js Performance: Leveraging Image and Link Components
Next.js has powerful built-in performance features. Learn how to use the Image, Link, and Font components for maximum speed.
Next.js is one of the fastest React frameworks because it includes automatic optimizations. But using the built-in components correctly makes the difference between a 60 and a 95 Lighthouse score.
The Image Component
The next/image component automatically:
- Serves AVIF/WebP based on browser support
- Resizes images to the exact dimensions needed
- Lazy loads images below the fold
- Prevents CLS by reserving space
- Generates responsive
srcsetfor different viewports
Basic Usage
import Image from 'next/image';
// Local image (automatically optimized at build time)
import heroImg from './hero.jpg';
<Image
src={heroImg}
alt="Hero section"
priority // Above-fold: no lazy loading, preloaded
placeholder="blur" // Shows blurred version while loading
/>
Remote Images
<Image
src="https://cdn.example.com/photo.jpg"
alt="Product photo"
width={600}
height={400}
quality={75}
loading="lazy"
/>
Critical Mistakes to Avoid
// ❌ BAD: No priority on hero image (delays LCP)
<Image src={heroImg} alt="Hero" />
// ✅ GOOD: Priority on above-fold LCP image
<Image src={heroImg} alt="Hero" priority />
// ❌ BAD: Using <img> tag (no optimization)
<img src="/photo.jpg" alt="Photo" />
// ✅ GOOD: Using Image component
<Image src="/photo.jpg" alt="Photo" width={800} height={600} />
// ❌ BAD: Oversized quality (unnecessary bytes)
<Image src="/photo.jpg" alt="" width={800} height={600} quality={100} />
// ✅ GOOD: Appropriate quality
<Image src="/photo.jpg" alt="" width={800} height={600} quality={75} />
The Link Component
next/link automatically prefetches linked pages when they enter the viewport:
import Link from 'next/link';
// Automatically prefetched when visible
<Link href="/pricing">View Pricing</Link>
// Disable prefetch for rarely-visited pages
<Link href="/terms" prefetch={false}>Terms of Service</Link>
How Prefetching Works
- Link becomes visible in viewport
- Next.js prefetches the page's JavaScript bundle
- On click, the page loads nearly instantly (no network wait)
When to Disable Prefetch
- Pages rarely visited (terms, privacy policy)
- Pages behind authentication
- Pages with many links (don't prefetch 100 pages)
The Font Component
next/font eliminates font-related CLS and removes external requests:
import { Inter, Outfit } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
display: 'swap',
variable: '--font-inter',
});
const outfit = Outfit({
subsets: ['latin'],
display: 'swap',
variable: '--font-outfit',
weight: ['400', '700', '900'],
});
export default function RootLayout({ children }) {
return (
<html className={`${inter.variable} ${outfit.variable}`}>
<body>{children}</body>
</html>
);
}
What next/font Does Automatically
- Self-hosts Google Fonts (no external request to fonts.googleapis.com)
- Adds
font-display: swapto prevent invisible text - Subsets the font to only include needed characters
- Preloads the font file
Server Components vs. Client Components
Server Components (default in App Router) improve performance by:
- Sending zero JavaScript to the browser for static content
- Reducing bundle size
- Enabling streaming
// Server Component (default) — no JS sent to browser
export default async function ProductPage({ params }) {
const product = await getProduct(params.id);
return <ProductDetails product={product} />;
}
// Client Component — only use when needed (interactivity)
'use client';
export default function AddToCartButton({ productId }) {
return <button onClick={() => addToCart(productId)}>Add to Cart</button>;
}
Rule: Keep components as Server Components unless they need:
useState,useEffect, or other hooks- Event handlers (
onClick,onChange) - Browser-only APIs
Streaming and Suspense
Load critical content first, stream secondary content:
import { Suspense } from 'react';
export default function Page() {
return (
<>
{/* Loads immediately */}
<HeroSection />
{/* Streams in when ready */}
<Suspense fallback={<ProductsSkeleton />}>
<ProductGrid />
</Suspense>
<Suspense fallback={<ReviewsSkeleton />}>
<Reviews />
</Suspense>
</>
);
}
Performance Monitoring
Next.js apps need continuous monitoring. BadPageSpeed tracks Lighthouse scores and Core Web Vitals for your deployed Next.js pages.
Ready to stop wasting ad spend?
Track your landing page performance, monitor Core Web Vitals, and calculate exactly how much slow pages cost you.
Start Free — No Credit Card