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.
Top comments (0)