External or remote image size compression / optimization in Next.js
Tags: nextjs, nextjsimage, imagecompression, imageprocessing
Published: 2024-01-09 07:37:37

Sometimes we need to reduce the size of the image that are getting from external API.As it’s just an url not the file & we use custom loader we don’t get the Next js Image optimization feature.Sometimes we had to implement prefix in the url and also prefix before static assests (“example/_next/static/….) we don’t get default next js image optimization feature if we use custom loader in the Image component.

First create a imageLoader.js file in top level of your project directory and paste the following code,


```
const imageLoader = ({ src, width, quality }) => {

  if (!src?.includes("https")) {
    return `${src}?w=${width}&q=${quality || 75}`; //for static images
  } else {
    src = src.split("https://").join(""); //will remove "https://" from the url to avoid CORS error
  }

  let secretUrl = encodeURIComponent(src); //encode image source url.

  const apiEndpoint = "/api/imageloader" //Write your internal(next js) api here

  return `${
    window?.location?.origin
  }${apiEndpoint}?url=${secretUrl}&w=${width}&q=${quality || 75}`;
};

export default imageLoader;
```


Now create an api for image optimization under api folder “api/imageloader.js” and paste the following code,we use redis for caching and reduce server pressure.If you want you can ignore redis.

```
import axios from "axios";
import sharp from "sharp";
import redis from "../../../lib/redis";


export default async function imageLoader(req, res) {
  let url = decodeURIComponent(req?.query?.url); //decoding the url
  url = `https://${url}`; //adding https:// that we were removed in the previos file

  try {

    if (!url) {
      return res.status(400).send("400 Bad request. url is missing");
    }

    const cachedUrl = await redis.getBuffer(`${url}`); //if already cached ther return cached one & reduce server load

    if (!cachedUrl) {
      const width = req.query.w ?? "384"; //default width
      const quality = req.query.q ?? "86"; //default quality
      // get the image information
      const response = await axios.get(url, {
        responseType: "arraybuffer",
      });

      // use sharp lib to resize the image based on the parameters
      const optimized = await sharp(response.data)
        .resize({
          withoutEnlargement: true,
          width: parseInt(width),
        })
        .webp({ quality: parseInt(quality) }) //transform image to webp format
        .toBuffer();

      // set public cache to 10  min
      res.setHeader("Cache-Control", "public, max-age=600, must-revalidate");
      
      // set content type to webp.
      res.setHeader("content-type", "image/webp");

      await redis.set(`${url}`, optimized);

      // send buffered image
      return res.status(200).send(optimized);
    }

    res.setHeader("content-type", "image/webp");
    return res.status(200).send(cachedUrl);

  } catch (e) {
    return res.status(e?.response?.status || 401).json({ message: e.message });
  }
}
```

Now we are ready to use our optimization api and loader.Pass the loader to the Image component.

 

```
<Image
    loader={imageLoader}
    src={imageUrl}
    alt="external image"
   />
```

We are good to go.Now enjoy your custom image optimization for next js external image.

Please feel free to share if this article helps you.