Optimizing Images

A quick guide on how to optimize your Next.js images to achieve light-speed performance.

Performance, Next.js
3 min read

Still loading...

Images are great for web design. Our brain loves images and is amazingly good at processing them. In fact, we can interpret images up to 60,000 times faster than text. The catch? Images can also sink your website's performance to a standstill if left unoptimized. In Next.js, you get powerful defaults for responsive images and automatic optimization, but there's still plenty you should do to guarantee fast loading, low data usage, and a smooth user experience. This short post walks through practical techniques to optimize images in Next.js.

Next.js Image component

Switch your <img> to the <Image /> provided by Next.js. This is component from next/image which solves many problems for you out of the box:

  • Responsive srcset: it generates multiple sizes and picks the best one for the device.
  • Automatic optimization: images are served in modern formats when possible (WebP / AVIF) and compressed on-the-fly.
  • Lazy-loading: images below the fold are deferred by default.
  • Preloading important images: use the priority prop for hero/LCP images so the browser fetches them early.
  • Placeholders: A blur placeholder will give you a low quality preview while the full image loads in. This is good for images for content that needs to load in.

Example usage:

page.tsx
import Image from "next/image";
 
export default function page() {
  return (
      <Image
        src="/myImage.png"
        alt="This is my image"
        width={1600}
        height={900}
      />
  );
}

When to prioritize?

Not every image needs priority. You should use priority on your Next.js Image components if it's:

  • in the hero or is an element above-the-fold. Essentially, if it is on your screen when you first visit the page, prioritize the image. This improves your Largest Contentful Paint and speeds up your initial load.

  • Key product or UI images that users see immediately on page load.

  • Images used in critical UI (e.g., a logo in the header should be prioritized).

Avoid prioritize background decorations, off-screen images, or tiny icons. For icons and simple graphics, use inline SVGs or an icon font — they're super lightweight and scale perfectly.

Implementing priority is simple with the Image component:

page.tsx
<Image
    src="/myImage.png"
    priority
/>

When to lazy load?

Lazy loading images should be used for images below the fold, or those not immediately visible. This technique defers loading non-critical images until they are needed (when the user scrolls), improving initial page load speed and reducing bandwidth usage. You can also add a blur placeholder while lazy loading your image.

page.tsx
<Image
    src="/myImage.png"
    loading="lazy"
    placeholder="blur"
    blurDataURL="/myImage.png"
/>

Lighthouse Audit

Use Lighthouse (Chrome > Inspect > Lighthouse) or PageSpeed Insights (for deployed websites) to measure the performance of your website. Key metrics to watch:

  • LCP (Largest Contentful Paint) — aim for < 2.5s on mobile, and < 1.5s on desktop, anything higher is likely due to a poorly-optimized image.

  • FCP (First Contentful Paint) — visible load speed.

  • CLS (Cumulative Layout Shift) — specify width/height on images loaded externally to avoid layout shifts.

  • Total Page Size & Requests — smaller images → fewer bytes transferred.

Practical checks:

  • Ensure hero images are priority

  • Ensure images loaded from external sources have correct width/height so layout is stable.

  • Run before/after Lighthouse audits to quantify gains.

  • Use the “Network” tab to confirm images served in WebP/AVIF when expected.

Final Notes

Small changes add up: serve modern formats, preload only the hero images, always set width and height (or use fill with a positioned container), and let Next.js handle responsive srcset. Run Lighthouse before and after your changes and aim for a real-world LCP under ~2.5s — your users (and Google) will thank you.

Back to All Snippets