next/image
A picture is worth a thousand words, and images are an important part of the web to communicate with users. Defining them in their most basic form is straightforward and done with the humble <img>
element:
<img src="image.jpg">
Ordinarily, the sourced image, image.jpg
, is embedded into a web page, assuming the image is in the same directory as the HTML page.
You can — and you should — go further in this canonical form of image definition by adding an alternative text (alt
) that describes an image if a browser or screen readers can’t load it:
<img src="image.jpg alt="describe the image here"/>
But with images on the web, the devil is in the detail. As the web evolved, so did the need for image optimization, whether it be for user or developer experiences.
To ensure users are served the most optimal image available, aspects like image size, web formats, and responsiveness must be addressed.
Solving for the user’s need is doable through the img
element’s plethora of APIs for optimization. However, they can quickly become unwieldy to unpack. This is where automatic image optimization can benefit developers.
Frameworks like Next.js offer an abstraction that solves the most common, mundane, and complex tasks like routing, internalization, and image optimization.
According to the Next.js team, the goal with Next.js is to improve two things: developer and user experiences. And while most of the optimization focuses on reducing the amount of JavaScript shipped to users, there are other aspects like images that need optimization as well. Enter Next.js 10.
Next.js 10 welcomed a built-in image optimization API, next/image
, as a canonical form for native automatic image optimization, providing five benefits.
With optimized images that are loaded lazily by default, users can expect a performance boost in website load time, ultimately improving the overall user experience.
With next/image
’s simple-to-use API, developers have an improved experience themselves with the ability to define a basic image, tweak it to their heart’s content, or delve into advanced configuration options like caching and loaders.
Build times aren’t increased as a side-effect of optimization because Next.js optimizes images on-demand as users request them, instead of at build-time.
Images are lazy-loaded by default and can be served in modern formats like WebP in supported browsers.
Next.js can also automatically adopt future image formats and serve them to browsers that support those formats.
next/image
APIThe next/image
API is the sweet spot of image optimization in Next.js. It exposes an <Image/>
component as a conventional single-source of truth. This means you only need to learn how to use one API to handle image optimization in Next.js.
By design, and in its most basic form, the <image/>
component is fundamentally similar to the HTML img
element because they both accept a src
and alt
attribute/property:
// 1. Import the `Image` component from the `next/image` API import Image from 'next/image'; export default function CardImage({imageSrc, imageAltText}) { return ( <div classname="cardImageWrapper"> {/* 2. Use the `Image` component as you would any other component */} <Image src={imageSrc} alt={imageAltText}/> </div> ) }
The functionality of the <Image/>
component can extend with a number of props available in it. Let’s take a look at them.
src
propThe src
is the single source of truth for the <Image/>
component:
<Image src="image.webp"/>
To use the image src
, it must be one of the following:
next.config.js
In this case, we’ll import a static image file:
import Image from 'next/image'; // Import a static image file import defaultCardImage from '../public/defaultCardImage.webp'; export default function CardImage({imageSrc = defaultCardImage, imageAltText}) { return ( <div classname="cardImageWrapper"> <Image src={imageSrc} alt={imageAltText}/> </div> ) }
width
and height
propsThe absolute width of an image is in pixels. This is required except for statically imported images or images with the layout prop set to fill
:
<Image src="image-src" alt="image-alt-text" width={40} height={40} />
loader
proploader
is a custom function that resolves URLs. Given the src
, width
, and quality
parameters, it returns a URL string:
import Image from 'next/image' // You can add as many loader as you want, and use them conditionally. // See https://nextjs.org/docs/basic-features/image-optimization#loader const sanityIoImageLoader = ({ src, width, quality }) => { return `https://cdn.sanity.io/${src}?w=${width}&q=${quality || 75}` } function CardImage() { return ( <Image loader={sanityIoImageLoader} src="image-src" alt="image-alt-text" width={500} height={500} /> ) }
sizes
propThe sizes
prop is akin to the HTML img
element sizes
attribute. With that, sizes are set to indicate a set of source sizes as described in the MDN docs:
<img src="image-src" alt="image-alt-text" srcset="(max-height: 500px) 1000px"/>
This means that if the viewport is less than 500px, use a source of 1000px width. With the <Image/>
component, you can specify the sizes
like the following:
<Image src="image-src" alt="image-alt-text" sizes="320 640 750" layout="responsive" />
Keep in mind that it is recommended to define sizes
only when using a responsive
or fill
layout.
quality
propThe quality
prop provides an integer between 1
and 100
that defines the quality of the optimized image; 1
being the worst quality and 100
being the best. It defaults to 75
:
<Image src="image-src" alt="image-alt-text" quality={100} layout="fill" />
priority
propBy default, images are not prioritized (because they are lazy-loaded), meaning priority
defaults to false
. When true
, the image is considered high-priority and preloaded.
This should only be used when the image is visible above the fold:
<Image src="image-src" alt="image-alt-text" width={500} height={300} priority />
layout
propThe layout
prop controls the layout behavior of the image as the size of the viewport changes. The accepted value is a string of either intrinsic
, fixed
, responsive
, or fill
.
Each of the layout values has their nuances in:
srcSet
and sizes
display
or position
value on the parent element for the corresponding <Image/>
element on which they’re usedLet’s take a look at the possible layout values
intrinsic
layoutThe intrinsic
layout is the default of the four layout values. Using this layout for smaller viewports scales down image dimensions while using it for larger viewports maintains the original image dimensions. You can see an example of intrinsic
layout here.
fixed
layoutThe fixed
layout is similar in behavior to the native img
element. With it, image dimensions remain fixed at their original dimensions regardless of viewport changes, meaning there is no response. You can see an example of a fixed
layout here.
responsive
layoutThe responsive
layout borrows from and overrides both the fixed
and intrinsic
layouts.
To override the fixed
layout, it responds to viewport changes, but to override the intrinsic
layout for larger viewports, where the image dimensions scale up, the parent element must be display: block
. You can see an example of a responsive
layout here.
fill
layoutThe fill
layout responds to its parent dimensions. With it, the image dimensions are stretched to the parent’s dimensions, provided the parent is position: relative
.
This is usually paired with the objectFit
property. You can see an example of a responsive
layout here.
placeholder
propThis placeholder
property is used as a fallback image when an image is loading. Its possible values are blur
or empty
.
placeholder
defaults to empty
, and when it is empty
, there is no placeholder while the image is loading, only empty space.
However, when it’s blur
, the blurDataURL
property is used as the placeholder. If the image src
is a static import with the MIME-type jpg
, png
, or webp
, the blurDataURL
automatically populates.
For dynamic images, you must provide the blurDataURL
property. Solutions such as Plaiceholder can help with base64
generation.
objectFit
propThe objectFit
prop is similar to the object-fit CSS property that sets how an image should resize to fit its container. It is used with layout=fill
or an image with a set width
and height
and has a possible value of contain
, cover
, fill
, none
, and scale-down
:
<Image src="image-src" alt="image-alt-text" layout="fill" objectFit="contain" />
objectPosition
propThis is similar to the object-position
CSS property that specifies the position of an image in its container. It is used with layout=fill
or an image with a set width
and height
and a position
value:
<Image src="image-src" alt="image-alt-text" layout="fill" objectPosition="top right" />
loading
propThe loading
prop is similar to the HTML img
element’s loading
attribute used for lazy loading. The possible value is a string of lazy
or eager
:
<Image src="image-src" alt="image-alt-text" layout="fill" loading="lazy" />
It is recommended that images above the fold be loaded with priority
instead of loading=“eager”
because this hurts performance.
onLoadingComplete
propThe onLoadingComplete
prop is a callback function that executes immediately after the image completely loads and the placeholder is removed:
<Image src="image-src" alt="image-alt" layout="fill" // returns: // {naturalWidth: <imageNaturalWidth>, naturalHeight: <imageNaturalHeight>} onLoadingComplete={(imageDimension) => console.log(imageDimension)} />
blurDataURL
propThe blurDataURL
prop is a placeholder image that loads before the src
image successfully loads and must be a base64-encoded data URL image that is effectual only when used in combination with placeholder=“blur”
:
import Image from 'next/image'; import cardImage from '../public/defaultCardImage.webp'; export default function CardImage() { return ( <div classname="cardImageWrapper"> <Image src={cardImage} alt="image-alt-text" placeholder="blur" // width, height, and blurDataURL are automatically provided /> </div> ) }
This data is automatically provided for statically imported images. For dynamic or remote images, you must provide width
, height
, and blurDataURL
manually.
You can see this example of the default blurDataURL
prop as well as the shimmer effect with blurDataURL
prop.
lazyBoundary
propThe lazyBoundary
prop is similar to rootMargin
in the Intersection Observer API, which acts as the marginal threshold for detecting intersections before triggering lazy loading. It defaults to 200px
.
unoptimized
propWhen the unoptimized
prop is true
, the src
image will be served as-is instead of changing quality, size, or format:
<Image src="image-src" alt="image-alt-text" width={700} height={450} unoptimized />
This prop defaults to false
.
Apart from the properties listed above, other properties passed to the <Image/>
component will relay to the underlying img
element with the exception of the following:
decoding
is always "async"
ref
, use onLoadingComplete
insteadsrcSet
, use deviceSizes
insteadstyle
, use className
insteadTo maintain the aspect ratio of an image and prevent the Core Web Vital and Cumulative Layout Shift, next/image
wraps the img
element with other div
elements.
To style the source image, name it with the className
prop then target it in your CSS:
<Image src="image-src" alt="image-alt-text" width={700} height={450} // You can style this image component with the `cardImage` class name className="cardImage" />
next/image
in next.config.js
next/image
can be configured via next.config.js. Let’s take a look at some of the configurations.
domains
By default, Next.js only optimizes images hosted on the same domain as your Next.js app. If your images are hosted externally on your CMS, for example, you must specify which domains are allowed to be optimized. This level of specificity is needed to prevent the abuse of external URLs:
module.exports = { images: { // assuming you were using the Sanity.io image CDN // domains is an array of comma-separated strings // ['cdn.sanity.io', 'cdn.not-sanity.io', 'another domain'] domains: ['cdn.sanity.io'], } }
Keep in mind that when loader
is set to an external image service, the domains
config is ignored.
loader
By default, Next.js handles image optimization but you can hand that responsibility over to a cloud provider like Cloudinary or imgix that is more dedicated to images than just general optimization.
To do this set the loader
and path
to allow the use of relative URLs:
module.exports = { images: { loader: 'cloudinary', path: 'https://your-site.com/assets/images/' } }
Caching is a rather intricate and, some say, one of the two hardest things in computer science.
In this context, caching speeds up image delivery to users because it avoids refetching over the network.
The Next.js docs have a sufficient explanation forcaching on the default loader. However, if you use a different loader, like Cloudinary, you must refer to that documentation to see how to enable caching.
While not always necessary, there are advanced use cases available for optimizing images. However, be aware that if configuring any properties in this section, it will override any change to the Next.js defaults in future updates.
Nevertheless, let’s see what they are.
deviceSizes
and imageSizes
Both device and image sizes are similar with subtle differences. They are both used on the merit that the device widths from the users of your website are known in advance. And they represent a list of device width breakpoints:
module.exports = { images: { // the sizes define below are the defaults deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840], imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], } }
deviceSizes
is effectual when the <Image/>
uses a responsive
or fill
layout and imageSizes
is effectual when the <Image/>
uses a fixed
or intrinsic
layout.
minimumCacheTTL
You can also configure how long or time-to-live (TTL) in seconds for cached optimized images with minimumCacheTTL
:
module.exports = { image: { // This is 60 seconds minimumCacheTTL: 60, } }
The docs advise that it is better to use a Static Image Import, which automatically handles hashing file contents and caching the file forever. But, keep in mind that you can disable static imports, so choose your battles wisely.
disableStaticImports
If you have your reasons (I haven’t personally come across one), it is possible to disable static imports:
module.exports = { images: { disableStaticImages: true } }
To ensure your users get the most optimal image on their devices, images are served with formats like WebP in supported browsers, allowing developers to serve lighter images to users to increase speed while preserving as much fidelity and quality as possible.
Different image formats have their use-cases. WebP is well supported and is a great choice for animated images, offering better compression than PNG or JPEG.
Since Next.js optimizes images in this format, the visual stability of a Next.js site isn’t bogged down by the effects of Cumulative Layout Shift and you can score high on Core Web Vitals.
Image optimization in Next.js improves the user and developer experience with a native game-changing and powerful API that’s easy to work with and extend. This inadvertently solves a major Core Web Vitals need and helps websites achieve a higher SEO rank, all starting and ending with next/image
.
Debugging Next applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens on your Next.js app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.
The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.
Modernize how you debug your Next.js apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]
One Reply to "Next.js automatic image optimization with <code>next/image</code>"
Anyone has problems with the quality prop? While my app running in dev mode all my served images are small in size. After the app got builded the pictures are served in original sizes. It seems that the quality prop doesn’t has a effect on it.