DEV Community

Cover image for External or remote image size compression / optimization in Next.js
Shafiul Azim
Shafiul Azim

Posted on

External or remote image size compression / optimization in Next.js

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;
Enter fullscreen mode Exit fullscreen mode

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 });
  }
}
Enter fullscreen mode Exit fullscreen mode

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"
   />
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)