Skip to main content

Command Palette

Search for a command to run...

๐Ÿš€ How to Optimize Next.js Apps with Image Optimization and Caching

Published
โ€ข4 min read
๐Ÿš€ How to Optimize Next.js Apps with Image Optimization and Caching

When building web applications, performance is everything. A slow-loading app can cost you users, SEO rankings, and ultimately revenue. Thankfully, Next.js comes with powerful built-in features to optimize images and caching.

In this blog, weโ€™ll cover:

  1. Why image optimization and caching matter

  2. Next.js Image component

  3. Setting up caching in Next.js

  4. Best practices to supercharge performance


๐Ÿ–ผ๏ธ Why Optimize Images?

Images usually take up 60โ€“70% of a pageโ€™s size. Without optimization, your app will load slower, especially on mobile and poor networks.

Benefits of optimizing images in Next.js:

  • Smaller file sizes โ†’ Faster load times

  • Responsive images โ†’ Better experience across devices

  • SEO boost โ†’ Faster sites rank higher


โšก Next.js Image Optimization with <Image />

Next.js provides an Image component that automatically:

  • Optimizes images on demand

  • Serves responsive images (srcset)

  • Uses modern formats like WebP when possible

Example: Basic Usage

import Image from "next/image";

export default function Hero() {
  return (
    <div className="flex flex-col items-center">
      <h1 className="text-3xl font-bold mb-4">Welcome to My App ๐Ÿš€</h1>
      <Image
        src="/hero.jpg"
        alt="Hero Banner"
        width={800}
        height={400}
        priority
      />
    </div>
  );
}

๐Ÿ‘‰ Here, Next.js automatically serves an optimized version of hero.jpg depending on device size.


Responsive Images with fill

<div className="relative w-full h-64">
  <Image
    src="/banner.jpg"
    alt="Responsive Banner"
    fill
    style={{ objectFit: "cover" }}
    sizes="(max-width: 768px) 100vw, 50vw"
  />
</div>
  • fill โ†’ Makes image cover parent container

  • sizes โ†’ Helps browsers decide which size to download


๐Ÿฅ‡ Enable priority for Above-the-Fold Images

Above-the-fold images are the ones visible immediately when the page loads (like a hero banner or logo). Using the priority prop tells Next.js to preload these images.

import Image from "next/image";

export default function HeroSection() {
  return (
    <div className="flex flex-col items-center justify-center h-screen">
      <h1 className="text-4xl font-bold mb-4">Welcome to My App ๐Ÿš€</h1>
      <Image
        src="/hero-banner.jpg"
        alt="Hero Banner"
        width={1200}
        height={600}
        priority   // Preloads this image
        className="rounded-lg shadow-lg"
      />
    </div>
  );
}

๐Ÿ‘‰ Use priority only on a few key images (like hero section or logo). Overusing it will hurt performance.


๐Ÿ”ƒ Lazy-Load Images (loading="lazy") for Below-the-Fold Content

For images not visible immediately (e.g., blog post thumbnails at the bottom), you should lazy-load them. This means the image is loaded only when the user scrolls near it, reducing initial page load time.

import Image from "next/image";

export default function BlogPosts() {
  const posts = [
    { id: 1, title: "Next.js Guide", img: "/next-guide.jpg" },
    { id: 2, title: "Supabase Auth", img: "/supabase-auth.jpg" },
    { id: 3, title: "Tailwind Tips", img: "/tailwind-tips.jpg" },
  ];

  return (
    <div className="grid grid-cols-1 md:grid-cols-3 gap-6 p-8">
      {posts.map((post) => (
        <div key={post.id} className="rounded-lg shadow-md p-4">
          <Image
            src={post.img}
            alt={post.title}
            width={400}
            height={250}
            loading="lazy"  // Loads only when in viewport
            className="rounded-md"
          />
          <h2 className="mt-2 text-xl font-semibold">{post.title}</h2>
        </div>
      ))}
    </div>
  );
}

๐Ÿ‘‰ Lazy loading is enabled by default in Next.js for images without priority, but adding loading="lazy" makes it explicit and clearer for readers.


๐Ÿ” Enabling Caching in Next.js

Caching ensures that once an asset is loaded, users donโ€™t have to re-download it again and again.

Static Assets Caching

Any file placed inside the public/ folder (e.g. /favicon.ico, /robots.txt) is served with long-term caching headers automatically.

API Route Caching Example

// app/api/posts/route.ts
import { NextResponse } from "next/server";

export async function GET() {
  const res = await fetch("https://jsonplaceholder.typicode.com/posts");
  const data = await res.json();

  return NextResponse.json(data, {
    headers: {
      "Cache-Control": "public, s-maxage=60, stale-while-revalidate=300",
    },
  });
}

Here:

  • s-maxage=60 โ†’ Cache for 60 seconds in CDN (like Vercelโ€™s Edge Network)

  • stale-while-revalidate=300 โ†’ Serve old cache while fetching fresh data


๐Ÿ› ๏ธ Best Practices

โœ… Use Next.js Image component instead of <img>
โœ… Set caching headers for APIs & assets
โœ… Enable priority for above-the-fold images
โœ… Lazy-load images (loading="lazy") for below-the-fold content
โœ… Serve WebP/AVIF when possible


๐Ÿ“Š Performance Check

After implementing these optimizations, run:

npx next build && npx next start

Then analyze with:

npx next dev --turbo

or test your site with Lighthouse (Chrome DevTools).

Youโ€™ll notice improved First Contentful Paint (FCP) and Largest Contentful Paint (LCP) scores.


๐ŸŽฏ Conclusion

Optimizing images and using proper caching strategies can drastically improve your Next.js appโ€™s performance. With just a few lines of code, you ensure:

  • Faster page loads

  • Better SEO

  • Happier users

Start by replacing <img> with <Image>, configure caching headers, and watch your app speed up ๐Ÿš€.


โœจ Thatโ€™s it for today! If you found this useful, share it with other developers and drop your feedback in the comments.