How To Optimize Images in Next.js with Next/Image Component
Blazity
Performance Optimization
In this article, we'll show why next/image is an excellent tool for managing images and solving issues like slow performance, high data usage, and slow rendering.
The Expert Guide to Next.js Performance Optimization
This article is part of The Expert Guide to Next.js Performance Optimization, a comprehensive Ebook you can download for free here.
The guide covers every aspect of building fast, scalable web apps with Next.js, including:
- Code Splitting
- Streaming, Suspense & Hydration
- Next/Image Component
- Third-Party Scripts
- Font Optimization
- Rendering Strategies
- Core Web Vitals
- Infrastructure
- Real-life Examples
- How to Measure Web Performance
- Bundle Analysis.
This article focuses on image optimization, a key factor in improving load times, reducing layout shifts, and boosting your app’s performance metrics.
We'll show why next/image is an excellent tool for managing images and solving issues like slow performance, high data usage, and slow rendering. You'll also discover how to use features like responsive sizing, image prioritization, placeholders, and blur effects to improve your visuals.
So, before we start implementing the next/image, the first question is why we should use it instead of the traditional img tag. The answer is simple—it provides all the essential optimizations out-of-the-box and simplifies implementing the more advanced ones.
Using the next/image is generally a good choice. However, if your app has a lot of images, using that feature on some hosting providers can get expensive. For example, Vercel charges for each optimized image.
What if you don’t want to use the next/image for some reason?
It isn't the only way to optimize images in Next.js. While it's user-friendly and makes maintaining your app easier, you can always use other standard methods for image optimization, such as:
fetchpriority
for critical images,srcset
manually.In fact, next/image uses these techniques under the hood but requires much less config, which makes it an excellent choice. For this reason, in this chapter, we’ll only focus on using the next/image.
Now, let's delve into some fundamental guidelines for optimizing images that help achieve optimal performance.
Managing how images load is crucial based on their importance and position on the page. For images that are immediately visible, known as "above the fold," you can use the priority attribute with the Next.js Image component to improve the loading strategy. This attribute instructs the browser to preload these images, deeming them high priority and skipping lazy loading.
Code
1import Image from "next/image";
2
3export const Page = () => (
4 <div>
5 <Image
6 src="/images/lcp-image.png"
7 alt="LCP Image"
8 priority
9 width={1000}
10 height={500}
11 />
12 {/* Below the fold content */}
13 </div>
14);
This approach is particularly useful for images contributing to the Largest Contentful Paint (LCP). The traditional img tag indeed has a similar attribute called "loading." But does setting it to "eager" achieve the same effect as the "priority" attribute in next/image?
What is the difference between loading=”eager” and the priority prop?
loading="eager"
: When applied to an HTML img tag, this attribute ensures that the image downloads immediately as the browser processes the HTML. The eager attribute signals the browser to prioritize fetching the image.<img src="path/to/image.jpg" alt="Description of image" loading="eager" />
priority
attribute: This attribute instructs Next.js to include a <link preload>
tag at the top of your page. This approach is more aggressive than plain HTML’s loading="eager"
because it prompts the browser to load the image even before the HTML is fully parsed, optimizing the loading process for critical resources.Code
1import Image from 'next/image';
2
3export const MyComponent = () => {
4 return (
5 <div>
6 <Image
7 src="/path/to/image.jpg"
8 alt="Description of image"
9 width={500}
10 height={300}
11 priority
12 />
13 </div>
14 );
15};
Some notes here: next/image also supports loading "eager|lazy,"
just like plain HTML. However, we advise applying it and recommend using the priority
prop instead. Remember that using the loading
prop on next/image is for advanced use cases and typically worsens performance.
In most cases, it doesn't make a noticeable difference. However, when pro grammatically adding images to the DOM, you should pay extra attention to the decoding property.
Using decoding="sync"
ensures that the <img/>
tag and the surrounding <div>
remain hidden until the decoding is complete. However, this approach will block other unrelated rendering processes for the next tick(s), which is the intended visual behavior.
On the other hand, decoding="async"
doesn't block other resources from (re)rendering. It also does not guarantee that the <img/>
and surrounding <div>
will be visible only after the image has finished loading.
For example, if the <div>
has an orange background, you will see a flash of orange, which is likely not the intended visual behavior. Choose the option that best fits your needs when programmatically adding elements to the DOM!
You may want to display the placeholder while the images load. It can be easily achievable using the `placeholder`
property from next/image. The placeholder can be one of the following values:
empty
- The default value set for all the images. This means that no placeholder is set and there is no visual representation of the loading state.blur
- When you specify it, the app displays a blurry version of the image during loading. When it comes to the dynamic images - here, along with the placeholder=”blur”
you must use the blurDataURL
property and specify the blurhash manually if your image hosting provider supports returning it from the server.The placeholders indicate to the user that the site is not broken and that something will be put in place for the temporary value in the image container. They also prevent some layout shifts.
Using modern image formats like WebP and AVIF in your web applications can significantly enhance performance by reducing file sizes and improving page load times. In the context of Next.js, this process is being streamlined with the Image component. It automates the delivery of images in the most efficient format supported by the user's browser.
Progressive JPGs load in stages, initially displaying a full but blurry image that gradually sharpens, unlike standard baseline JPGs, which load from top to bottom.
In case of:
Progressive JPGs can be a better choice than WebP due to their perceived loading speed advantage.
How Automatic Image Optimization Works in Next.js?

Using the next/image module component automatically converts images into more efficient formats, depending on browser support. This means that even if the original image is in JPEG or PNG format, Next.js can serve it in a format that results in smaller file sizes and (in most scenarios) faster loads.
Code
1import Image from 'next/image'; export const Component = () => (
2<div>
3 <image src="https://example.com/path/to/your/image.jpg" alt="example" priority />
4</div>
5);
6
So, if we quickly revisit the previous example, it's important to note that "example.png" will be served as a WebP image instead of a PNG file.
Code
1import Image from 'next/image'; const Page = () => (
2<div>
3 <image src="/images/example.png" alt="example" priority />
4 <image src="/images/example.png" alt="example" />
5</div>
6); export default Page
7
Worth Knowing
By default, Next.js will use only WebP format.
AVIF has to be enabled explicitly. It is important to actively pick the best im age format for your application, as this is often overlooked.
Fine-Grained Image Format Control
While automatic image optimization is beneficial, you might want to disable this behavior in some situations.
For example:
Here are some scenarios when automatic optimization might not be suitable:
To disable automatic image optimization in Next.js, you can use the unoptimized
prop in the next/image
component. This prop instructs Next.js to serve the image in its original format without any conversion or optimization.
Code
1import Image from 'next/image'
2
3export const Component = () => {
4 return (
5 <div>
6 <Image
7 src="/path/to/your/image.jpg" // Original image path
8 alt="Description of image"
9 width={500}
10 height={300}
11 unoptimized // Disable automatic optimization
12 />
13 </div>
14 )
15}
Correctly sizing images is vital for optimal web performance, minimizing layout shifts, and ensuring visuals appear sharp on various devices. The next/image simplifies this task, offering several methods to specify image dimensions and behavior depending on specific requirements.
Three Approaches to Image Sizing in Next.js:
1. Automatic Sizing for Static Imports
Next.js can automatically determine the dimensions of locally stored images imported statically, helping the browser reserve the space needed before the image loads. This approach prevents layout shifts and ensures smooth rendering.
Code
1import Image from 'next/image';
2import teamPhoto from '../public/team.jpg'; // Static import of an image
3
4export const Home = () => {
5 return (
6 <div style={{ width: '500px', height: 'auto' }}>
7 <h1>Meet Our Team</h1>
8 <Image
9 src={teamPhoto}
10 alt="Our team standing together"
11 />
12 </div>
13 );
14}
2. Explicit Sizing with Width and Height Properties
Set explicit width and height for remote images or fixed dimensions to help the browser allocate the right space. This straightforward method ensures images are rendered at the intended size, avoiding unexpected layout shifts.
Code
1import Image from 'next/image';
2
3export const ProductPage = () => {
4 return (
5 <div style={{ width: '800px', margin: 'auto' }}>
6 <h2>Featured Product</h2>
7 <Image
8 src="https://example.com/product.jpg"
9 alt="Newest model of a high-tech gadget"
10 width={800}
11 height={600}
12 />
13 </div>
14 );
15};
3. Implicit Sizing with the Fill Property
The fill property allows an image to expand and cover its parent container, making it ideal for dynamic sizing or adapting to varying display dimensions. It's particularly useful when the exact image size isn't known in advance and must adjust to the layout dynamically.
Code
1import Image from 'next/image';
2
3export const Banner = () => {
4 return (
5 <div style={{ width: '100%', height: '400px', position:
6 'relative' }}>
7 <Image
8 src="/banner.jpg"
9 alt="Promotional banner image"
10 fill
11 style={{ objectFit: 'cover' }} // Ensures the image covers
12 the full area of the container
13 sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw,
14 33vw"
15 />
16 </div>
17 );
18};
19
Why use the sizes attribute with the fill layout?
The sizes attribute is essential when using the fill layout, as it tells the browser which image size to load based on the screen resolution.
Without the sizes attribute, the browser may download a larger image than necessary, leading to longer load times and higher data usage. By setting conditions such as (max-width: 768px) 100vw, (max-width: 1200px) 50vw, or 33vw, you ensure the browser loads an appropriately sized image, preventing excessive bandwidth usage and improving performance.
Note: Without the 33vw size setting, the browser would select an image three times wider than needed. Since file size grows with the square of the width, the user would end up downloading an image nine times larger than necessary.
Next.js can automatically handle resizing for full-width images, but explicitly defining sizes can improve performance.
4. deviceSizes and imageSizes in the project configuration
If you know the types of devices your users have, you can configure deviceSizes
and imageSizes
to ensure images are resized correctly for optimization. These configuration properties serve different purposes, which are explained below:
deviceSizes
: Defines breakpoints for full-width images, with a default set of [640, 750, 828, 1080, 1200, 1920, 2048, 3840]
. Use this when targeting specific device widths. These breakpoints help Next.js choose the appropriate image size based on the device’s viewport width.imageSizes
: Used for images smaller than the viewport width (when using the 'sizes' prop). The default values are [16, 32, 48, 64, 96, 128, 256, 384]
, and each must be smaller than the smallest value in deviceSize.In summary, sizing images appropriately with Next.js's Image component prevents layout shifts and optimizes loading times and bandwidth usage, ensuring your website performs well across all devices and resolutions.
Setting the right image quality is essential to optimizing your web application's performance and user experience. While the default quality setting of 75 typically strikes a good balance between file size and visual experience, there are situations where adjusting this parameter can further enhance your website.
Pros & Cons of Manipulating Image Quality:
Pros:
+ Enhanced Visuals: Higher quality ensures that images look crisp and detailed on high-definition displays or when they are a focal point (like in a photography portfolio).
+ Brand Image: High-quality images can reflect more professionally on your brand, which might be critical in e-commerce and digital marketing.
+ User Experience: Sharper, more vibrant images can improve user engagement and satisfaction, potentially leading to longer site visits.
Cons:
- Longer Loading Times
- More Bandwidth Usage
- Visual Artifacts: Reduced quality can introduce noticeable imperfections like pixelation or compression artifacts, potentially impacting the image's overall visual appeal.
Here's how to explicitly set the image quality in Next.js to tailor the user experience based on your specific needs.
Code
1import Image from 'next/image';
2
3export const SimpleImage = () => {
4 return (
5 <Image
6 src="/path-to-your-image.jpg" // Replace with your image path or URL
7 alt="A description of the image"
8 width={800}
9 height={500}
10 quality={80} // Set image quality from 0 to 100. Default is 75.
11 />
12 );
13}
14
Considerations:
Note:
These considerations apply to raster images (e.g., JPEG, WebP, PNG). The default loader generally does not optimize vector images (e.g., SVG) because they can be resized without losing quality. Additionally, SVG optimization is limited due to security concerns related to the format.
If you want to use next/image
for SVGs, the unoptimized
flag is recommended, but Next.js also turns optimization off automatically when the image’s src ends with “.svg.”
unoptimized
prop to skip optimization for specific images.Get a quote
“We are very happy with the outcomes and look forward to continuing to work with them on larger initiatives.”
Brian Grafola
CTO at Vibes