Image Optimization with NextJS
Tags: javascript, webdev, programming, react
Published: 2022-05-06 00:14:50

**NextJS** is fast becoming my favorite frontend framework because of the endless advantages over a basic React application, one of those said benefits would be the Built-in Image Component.

In this article, we would take a look at the Image component from [NextJS](https://nextjs.org/) and learn how to use it to optimize an image in our web app.

By the end of this article, you should understand the following concepts:
- Image Optimization
- Using `next/image`
- Image Props
- Configuring `next.config.js`
- Usage of the native `< img >` tag in NextJS


## Image Optimization

Ordinarily, if you were going to make use of an image in your website/app you would do this ( assuming the image is in the same directory as the webpage it is accessing ):
```javascript
<img src="./apple.jpg">
```

You can go further by adding an alternative text (for screen readers or when the image cannot be loaded) by doing this: 

```javascript
<img src="./apple.jpg" alt="Image of an apple"/>
```

However, this format doesn't solve image optimization aspects like image size, web formats, and responsiveness with this single usage. 

**NextJS** offers automatic image optimization which solves all of the above as well as common tasks like internalization and routing.

The golden rule for any performance optimization simply put is giving users what they want in the shortest possible time or providing a fall-back if need be.

Hence **NextJS** provides us with a built-in image optimization API, `next/image`, a canonical form for native automatic image optimization.

## Using `next/image`

The Image component in **NextJS** is quite similar to the native html `<img>`, it's an extension of this element and can be used by importing it from `next/image` and using it like you'd use a component with props. 

```javascript
import Image from 'next/image';

export default function SampleImage({source}) {
    return (
        <div>
            <Image src={source} alt='Image alt text'/>
        </div>
    )
} 

```

The Image tag has a couple of props available to it for use asides the src and alt prop, we'd take a look at some of them 

### `width` and `height` prop
The width and height of the image is in *pixels* , when adding the width and height be sure to add the correct dimension. If a different aspect ratio is added, the image would adjust accordingly. For example if the width and height of a (1400 x 700) image gets changed to (400 x400) as shown below, it could result in a skewed image. 

```jsx
import Image from 'next/image';

export default function SampleImage({source}) {
    return (
        <div>
            <Image 
               src={source} 
               alt='Image alt text'
               height={400}
               width={400}
             />
        </div>
    )
} 
```

### `layout` prop
There may be times you do not know the width and height of an image, but still want it to fill the entire space while keeping its aspect ratio. In this situation, you can leave out the width and height prop on the Image component. Instead, add a prop of `layout="fill"`. This will stretch the image to the width and the height of the parent element. When using the `layout="fill"` prop, it is often best to pair it with `objectFit="cover"`. This will allow the image to maintain its aspect ratio while filling the element’s entire content box.
To achieve this, wrap the Image component as a child of a `<div>` element. Then add a width and height to the parent `<div>` element, along with giving it a `position="relative"`.


```jsx
import Image from 'next/image';

export default function SampleImage({source}) {
    const myStyle = {
       height: '400px',
       width: '400px',
       position: 'relative' 
   }
    return (
        <div style={myStyle}>
            <Image 
               src={source} 
               alt='Image alt text'
               layout='fill'
               objectFit='cover'
             />
        </div>
    )
} 

```

This way, we can see that the image is taking up the 400-pixel square that we wanted, but the aspect ratio of the image is still in place. The parts of the image that do not fit within the parent element are clipped.

Other `layout` values are intrinsic, fixed, and responsive.

### `loader` prop
A loader is a function returning a URL string for the image, given the following parameters (`src`, `width`, `quality`). Setting the loader as a prop on the Image component overrides the default loader defined in the images section of `next.config.js`.

```jsx
import Image from 'next/image'

const sampleLoader = ({ src, width, quality }) => {
  return `https://example.com/${src}?w=${width}&q=${quality || 75}`
}

const MyImage = (props) => {
  return (
    <Image
      loader={sampleLoader}
      src="me.png"
      alt="My Picture"
      width={500}
      height={500}
    />
  )
}
```




### `sizes` prop
You can specify a list of image widths using the `images.imageSizes` property in your `next.config.js` file. These widths are concatenated with the array of device sizes to form the full array of sizes used to generate image srcsets.

```jsx
module.exports = {
  images: {
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
  },
}
```
Or by defining it in your component like,

```jsx
<Image
    src={src}
    alt="image-alt-text"
    sizes="320 640 700"
    layout="responsive"
/>
```

Keep in mind that it is recommended to define `sizes` only when using a `responsive` or `fill` layout.

### `quality` prop
The quality of the optimized image, is an integer between `1` and `100` where `100` is the best quality. Defaults to `75`.
```jsx
<Image
    src={src}
    alt="image-alt-text"
    quality={100}
    layout="fill"
/>
```

### `priority` prop
By 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.
You should use the `priority` property on any image detected as the Largest Contentful Paint (LCP) element. 
Should only be used when the image is visible above the fold. Defaults to `false`.

```jsx
<Image
    src={src}
    alt="image-alt-text"
    width={500}
    height={300}
    priority
/>
```
### `placeholder` prop
This `placeholder` property is used as a fallback image when an image is loading. Its possible values are `blur` or `empty`.
When `empty`, there will be no placeholder while the image is loading, only empty space. When `blur`, the `blurDataURL` property will be used as the placeholder. If `src` is an object from a static import and the imported image is .jpg, .png, .webp, or .avif, then blurDataURL will be automatically populated.

```jsx
<Image
    src={src}
    alt="image-alt-text"
    width={500}
    height={300}
    placeholder="blur"
/>
```

### `blurDataURL` prop
The `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”`.

```jsx
<Image
  src={src}
  alt="image-alt-text"
  width={600}
  height={450}
  placeholder="blur"
  blurDataURL=”data:image/png;base64,[IMAGE_CODE_FROM_PNG_PIXEL]”
/>
```

### `objectFit` prop
The `objectFit` prop defines how the image will fit into the container of it's parent, quite similar to the [object-fit CSS property](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit). It is used with `layout=fill` or an image with a set `width` and `height`.

```jsx
<Image 
    src={src}
    alt="image-alt-text"
    layout="fill"
    objectFit="contain"
/>
```
It has a possible value of: `contain`, `cover`, `fill`, `none`, and `scale-down`.

### `unoptimized` prop
When `true`, the source image will be served as-is instead of changing quality, size, or format. Defaults to `false`.
```jsx
<Image
    src={src}
    alt="image-alt-text"
    width={700}
    height={450}
    unoptimized
/>
```

## Configuring `next.config.js`
You can configure your **NextJS** image through the `next.config.js` file 

### `domains`
When using an external URL to load images, you must add it to `domains` in `next.config.js`
```jsx
module.exports = {
    images: {
        domains: ['example.com'],
    }
}
```
### `loader`
By default, **NextJS** handles image optimization but you can hand that responsibility over to a cloud provider such as [Cloudinary](https://cloudinary.com/) or [imgix](https://imgix.com/) that is more dedicated to images than just general optimization.
```jsx
module.exports = {
    images: {
        loader: 'cloudinary',
        path: 'https://your-site.com/assets/images/'
    }
}
```
Keep in mind that when `loader` is set to an external image service, the `domains` config is ignored.

For more advanced cases of props in **NextJS**, there are other props that you can add to the Image component as well as configurations. Check out the full [documentation here](https://nextjs.org/docs/api-reference/next/image). 


## Conclusion
Image optimization in Next.js improves the user and developer experience but just like every other thing in programming, the Image component has some limitations one of which is its inability to adjust *CSS* directly. Unlike the native `<img>` element whereby you can pass a `style` prop to override its *CSS*. The **NextJS** image component doesn't support the `style` property at all. Hence to style the source image, name it with a `className` then target it with your CSS.

```jsx
<Image
    src={src}
    alt="image-alt-text"
    width={700}
    height={450}
    className="myImage"
/>
```
P.S: Next.js forces using their <Image> component instead of the native `<img>` tag by including the corresponding linter check to the app build process. So if you're going to make use of the `<img>` tag in a NextJS application you'd add the following to disable the check 

```jsx
// eslint-disable-next-line @next/next/no-img-element
 <img
     src={src}
     alt='myImg'
     className='myImage'
 />
```
Or by adding `"@next/next/no-img-element": "off"` in the `.eslintrcconfig` file.


> I hope you enjoyed reading this as much as I enjoyed writing it and would be building your NextJS app as soon as possible.

Resources used:
- [NextJS Doc](https://nextjs.org/docs/api-reference/next/image)
- [Log Rocket](https://blog.logrocket.com/next-js-automatic-image-optimization-next-image/)
- [UploadCare](https://uploadcare.com/blog/next-js-image-optimization/)
- [Level Up Coding](https://levelup.gitconnected.com/optimize-images-from-an-external-website-with-the-image-component-from-next-js-aa104a1f78e0)

👉🏾 [Learn more about me](https://www.abdulqudus.com/) 

👉🏾 [Connect on LinkedIn](https://www.linkedin.com/in/jideabdqudus/) 

👉🏾 [Subscribe to my blog, let's feast](https://blog.abdulqudus.com/)