Yago.io brand

Understanding Next/Image

Yann Gouffon — April 5th, 2021

With Next.js version 10, a new image component was introduced to offer modern format and on-demand optimisation for images as well. If you came from Gatsby, it’s the Next.js answer to gastby-image and it just rocks!

Images are always rendered in such a way as to avoid Cumulative Layout Shift, a Core Web Vital that Google is going to use in search ranking.

Weirdly, it’s so simple and magic that I was struggling to know how to use it. Also, the doc is not great to really understand how it works and to advise you to the most common practice: responsive user interface.

Let’s dive in

If you read the short documentation page about next/image , the only example you will see is basically this one.

<Image
  src="/my-16-by-9-big-image.jpg"
  alt="Picture of something nice"
  width={1000}
  height={1000}
/>

It’s an easy mistake to think that our output will be a 1000 by 1000 pixels image (and a 2x for retina), but instead we’ve got a 1080 × 608 pixels image stretched in a square… So before going further, we need to understand few concepts about this component.

The layouts

There is four different layout available for your <Picture /> :

  • layout="intrinsic" is the default value; it’s basically a CSS max-width. In our example, the image will be 1000 pixels wide on desktop and will fit to its parent width on mobile, but the resolution will not change, only the displayed size.
  • layout="fixed" is pretty self-explanatory; just like a CSS width, the image will be displayed in the defined sizes without any resizing. Here 1000 by 1000 pixels, but the resolution is still 1080 × 608 pixels.
  • layout="responsive" is the magic and maybe the main one to remember, because we are in 2021 and performances matter. This time it’s more like a CSS width: 100%;, but unlike the first two above, it will provide a large number of different resolutions in the srcset output.
  • layout="fill" is useful when you don’t want (and don’t need) to define a width and a height. It’s a standard CSS object-fit. For this variant only, you will be able to add the extra objectFit and objectPosition properties to your component; they work just like the related CSS specification. It also provides a responsive srcset.

What you must remember:

  • The width and height properties must match your image ratio, unless you are using layout="fill"
  • intrinsic (by default) and fixed are used for static resolutions and responsive and fill for responsive resolutions.
  • If you know the image size, use responsive and if you don’t, use fill

The resolutions

As you can see in our example, the resolution doesn’t quite match the size. It’s because there are two *options in your next.config.js that you must be aware of:

  • deviceSizes are large breakpoints
  • imageSizes are small breakpoints

By default, these options are set this way:

module.exports = {
  images: {
    deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
  },
}

These two arrays are merged to form a complete collection of potential widths. So, if like me, you want a picture resized precisely to 1000 by 1000 px (and 2000 for retina), these sizes must be present in the deviceSizes array.

** There are also other options to allow external images or to define a custom loader.*

Other available props

Apart from the main things to know when using next/image, there are some useful properties to be aware of:

  • loader is useful to pass a loader component
  • sizes is to override the default sizes of 100vw, useful only when layout="responsive" or layout="fill" are defined
  • quality can be used to override the default quality of 75
  • priority must be used only when the image is visible above the fold. It’s false by default.
  • loading is lazy by default, but can be changed to eager to force an immediate loading.
  • unoptimized if you want something close to the default <img /> behaviour.
  • You can also use style, srcSet and decoding to directly target the <img />

Examples

Now that we better understand how next/image works and how it can be configured, here is some real-life examples.

Classic responsive

Example of a responsive image inside a blog post content.

<Image
  src="/my-16-by-9-big-image.jpg"
  alt="Picture of something nice"
  width={800}
  height={450}
  layout="responsive"
  quality={65}
/>
{/* Or for the same result */}
<Image
  src="/my-16-by-9-big-image.jpg"
  alt="Picture of something nice"
  width={16}
  height={9}
  layout="responsive"
  quality={65}
/>

Automatic responsive

As we quickly saw earlier, the size property is 100vw by default. If you want a perfect match to your image size, you can use something like react-cool-dimensions and wrap the <Picture /> into a new component.

import React from 'react';
import useDimensions from 'react-cool-dimensions';
import Image from 'next/image';

export default (props): JSX.Element => {
  const { observe, width } = useDimensions<HTMLDivElement | null>();

  return (
    <div ref={observe}>
      <Image
        {...props}
        layout="responsive"
        quality={65}
        sizes={width !== undefined ? `${Math.round(width)}px` : '100vw'}
      />
    </div>
  );
};

Unknown/variable size with fixed ratio

Sometimes pictures came from an outside source (CMS, APIs, etc) and the image size (width, height) is not available or the ratio can vary. For this case, the layout="fill" is very useful with something like @tailwindcss/aspect-ratio.

<div className="aspect-w-16 aspect-h-9">
  <Image
    src="/my-big-variable-image.jpg"
    alt="Picture of something nice"
    layout="fill"
    objectFit="cover"
  />
</div>

Unknown/variable size with variable ratio

Sadly for the moment, there is no way to respect an image ratio without knowing its width and height or at least, its ratio. You can still define an area where the image will be rendered without crop. For example, here, inside a gray square.

<div className="aspect-w-1 aspect-h-1 bg-gray-100">
  <Image
    src="/my-big-variable-image.jpg"
    alt="Picture of something nice"
    layout="fill"
    objectFit="contain"
  />
</div>

Conclusion

next/image is a great add to the Next.js ecosystem, especially for people coming from Gatsby. It provides a very simple way to add the responsive layers to your images without any complicated backend configuration. It’s another great example of the capabilities of clients to manage the media from a frontend perspective. Let your backend deal with the sources images and let your client application deal with the size needed by the consumers.