External or remote image size compression / optimization in Next.js
Shafiul Azim
Posted on January 9, 2024
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.
Posted on January 9, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.