Pass height param to custom next/image loaders #22050
Replies: 10 comments 4 replies
-
Just wanted to chime and say that I'm running into this as well. We have an external service that needs both height and width. I think it would make sense to pass all of the props into the loader. |
Beta Was this translation helpful? Give feedback.
-
I arrived here trying to understand why the height isn't passed. I guess it is not that useful if the aspect ratio isn't changing and therefore the server can infer it based on the original image dimensions. Anyway, I ended up creating the loader function within a wrapper component of function Image({ aspectRatio, width, ...nextImageProps }) {
const height = calcAspectRatio(width, aspectRatio);
const loader = ({ width: w, src }) => {
const h = calcAspectRatio(w, aspectRatio)
return `${src}?w=${w}&h=${h}`
}
return (
<NextImage
{...nextImageProps}
width={width}
height={height}
loader={loader}
/>
);
} Any better ideas? PS: You may need this if you are dealing with TypeScript and Next Image type definitions. |
Beta Was this translation helpful? Give feedback.
-
We're also looking into this and I don't see any reason why the height should not be passed (if available). |
Beta Was this translation helpful? Give feedback.
-
Yeah, I am also running into a situation where I need height to be available within the custom loader function. It would be great if the next team would add that. 👍 |
Beta Was this translation helpful? Give feedback.
-
I am unable to use Next/Image with some cool features on Imgix like entropy cropping as the width and height are required. https://docs.imgix.com/apis/rendering/size/crop#entropy |
Beta Was this translation helpful? Give feedback.
-
any ideas on this? |
Beta Was this translation helpful? Give feedback.
-
just came here looking for a solution as well. Falling back to the solution provided here. |
Beta Was this translation helpful? Give feedback.
-
Any update on this please ? |
Beta Was this translation helpful? Give feedback.
-
Yes, the height prop is needed for cropping images served by unsplash (and countless other image transformation services). I'm guessing the assumption was that height would not be needed for the "resolution switching" use case for responsive images, but it is 100% needed for images served with crop parameters, here's an example: https://images.unsplash.com/photo-1515825838458-f2a94b20105a?auto=format&fit=crop&w=1440&h=768&q=80&crop=entropy I suppose the responsive images use-case here defines the treatment of the height value, and it can be derived from the width using a calculation based on aspect ratio, maybe the expectation is that developers come up with a way to do that, which needs an explanation perhaps in the docs or some kind of demonstration. It could be more intuitive—considering that a width and a height gets passed to the Image component—that, like width, an optional height can be automatically taken via the Image component's props, you'd only really want to define these values in 1 place so that they can be used for all calculations. For context, in my case I'm defining the image in frontMatter (MDX) like so:
The aspect ratio is calculated from the width and height: const aspectRatioHeight = (
originalWidth: number,
originalHeight: number,
width: number
): number => Math.round((originalHeight / originalWidth) * width);
const params = new URLSearchParams(heroImage?.split("?").at(-1));
const originalWidth = Number(params.get("w"));
const originalHeight = Number(params.get("h"));
const loader = ({ src, width }: { src: string; width: number }) => {
const photo = src.split("?").at(0);
const height = aspectRatioHeight(originalWidth, originalHeight, width);
return `https://images.unsplash.com/${photo}?auto=format&fit=crop&w=${width}&h=${height}&q=80`;
};
<Image
className={s.heroImage}
alt=""
width={originalWidth}
height={originalHeight}
src={heroImage}
loader={loader}
sizes="(max-width: 768px) 50vw, 100vw"
/> |
Beta Was this translation helpful? Give feedback.
-
Sharing this without any warranties, use it as you please. diff --git a/dist/client/legacy/image.js b/dist/client/legacy/image.js
index 899255364dd2177b4bfd50b3aa5c05393b7c71de..d5216bf21bcc7d87a2cd8dfc5e03413f652cb78a 100644
--- a/dist/client/legacy/image.js
+++ b/dist/client/legacy/image.js
@@ -651,6 +651,7 @@ function Image(param) {
unoptimized,
layout,
width: widthInt,
+ height: heightInt,
quality: qualityInt,
sizes,
loader
diff --git a/dist/esm/client/legacy/image.js b/dist/esm/client/legacy/image.js
index 50bde918e0217abfe79d46f705871945a0c57a57..dff01f5c20d5b0d291b47ea0e5b4004472c9ec4d 100644
--- a/dist/esm/client/legacy/image.js
+++ b/dist/esm/client/legacy/image.js
@@ -357,6 +357,7 @@ const ImageElement = (param)=>{
unoptimized,
layout,
width: widthInt,
+ height: heightInt,
quality: qualityInt,
sizes: noscriptSizes,
loader
diff --git a/dist/esm/shared/lib/get-img-props.js b/dist/esm/shared/lib/get-img-props.js
index d2835105b71ebae08349c232a0cf839df0643ab8..31fe95448978d6a96622a9f934df76f12c98888a 100644
--- a/dist/esm/shared/lib/get-img-props.js
+++ b/dist/esm/shared/lib/get-img-props.js
@@ -76,7 +76,7 @@ function getWidths(param, width, sizes) {
};
}
function generateImgAttrs(param) {
- let { config, src, unoptimized, width, quality, sizes, loader } = param;
+ let { config, src, unoptimized, width, height, quality, sizes, loader } = param;
if (unoptimized) {
return {
src,
@@ -92,7 +92,8 @@ function generateImgAttrs(param) {
config,
src,
quality,
- width: w
+ width: w,
+ height: height ? Math.round(w * (height / width)) : undefined
}) + " " + (kind === 'w' ? w : i + 1) + kind).join(', '),
// It's intended to keep `src` the last attribute because React updates
// attributes in order. If we keep `src` the first one, Safari will
@@ -104,7 +105,8 @@ function generateImgAttrs(param) {
config,
src,
quality,
- width: widths[last]
+ width: widths[last],
+ height: height ? Math.round(widths[last] * (height / width)) : undefined
})
};
}
@@ -308,6 +310,7 @@ function generateImgAttrs(param) {
config,
src,
width: widthInt || 400,
+ height: heightInt || 400,
quality: qualityInt || 75
});
let url;
@@ -397,6 +400,7 @@ function generateImgAttrs(param) {
src,
unoptimized,
width: widthInt,
+ height: heightInt,
quality: qualityInt,
sizes,
loader
diff --git a/dist/esm/shared/lib/image-loader.js b/dist/esm/shared/lib/image-loader.js
index a5eb2652b29742d14c60b3e7ef5bd9f23e1a1c7b..a941fb8f736b96eadda2392311e097f4d0f1b75a 100644
--- a/dist/esm/shared/lib/image-loader.js
+++ b/dist/esm/shared/lib/image-loader.js
@@ -1,14 +1,16 @@
function defaultLoader(param) {
- let { config, src, width, quality } = param;
+ let { config, src, width, height, quality } = param;
if (process.env.NODE_ENV !== 'production') {
const missingValues = [];
// these should always be provided but make sure they are
if (!src) missingValues.push('src');
if (!width) missingValues.push('width');
+ if (!height) missingValues.push('height');
if (missingValues.length > 0) {
throw new Error("Next Image Optimization requires " + missingValues.join(', ') + " to be provided. Make sure you pass them as props to the `next/image` component. Received: " + JSON.stringify({
src,
width,
+ height,
quality
}));
}
@@ -43,7 +45,7 @@ function defaultLoader(param) {
}
}
}
- return config.path + "?url=" + encodeURIComponent(src) + "&w=" + width + "&q=" + (quality || 75) + (process.env.NEXT_DEPLOYMENT_ID ? "&dpl=" + process.env.NEXT_DEPLOYMENT_ID : '');
+ return config.path + "?url=" + encodeURIComponent(src) + "&w=" + width + "&h=" + height + "&q=" + (quality || 75) + (process.env.NEXT_DEPLOYMENT_ID ? "&dpl=" + process.env.NEXT_DEPLOYMENT_ID : '');
}
// We use this to determine if the import is the default loader
// or a custom loader defined by the user in next.config.js
diff --git a/dist/shared/lib/get-img-props.js b/dist/shared/lib/get-img-props.js
index 99ad3535659e6c066de4d1676e942b5167ee1f55..a61fcbfe6eefb381916da5267c3220578ed79594 100644
--- a/dist/shared/lib/get-img-props.js
+++ b/dist/shared/lib/get-img-props.js
@@ -86,7 +86,7 @@ function getWidths(param, width, sizes) {
};
}
function generateImgAttrs(param) {
- let { config, src, unoptimized, width, quality, sizes, loader } = param;
+ let { config, src, unoptimized, width, height, quality, sizes, loader } = param;
if (unoptimized) {
return {
src,
@@ -102,7 +102,8 @@ function generateImgAttrs(param) {
config,
src,
quality,
- width: w
+ width: w,
+ height: height ? Math.round(w * (height / width)) : undefined
}) + " " + (kind === 'w' ? w : i + 1) + kind).join(', '),
// It's intended to keep `src` the last attribute because React updates
// attributes in order. If we keep `src` the first one, Safari will
@@ -114,7 +115,8 @@ function generateImgAttrs(param) {
config,
src,
quality,
- width: widths[last]
+ width: widths[last],
+ height: height ? Math.round(widths[last] * (height / width)) : undefined
})
};
}
@@ -316,6 +318,7 @@ function getImgProps(param, _state) {
config,
src,
width: widthInt || 400,
+ height: heightInt || 400,
quality: qualityInt || 75
});
let url;
@@ -405,6 +408,7 @@ function getImgProps(param, _state) {
src,
unoptimized,
width: widthInt,
+ height: heightInt,
quality: qualityInt,
sizes,
loader
diff --git a/dist/shared/lib/image-loader.js b/dist/shared/lib/image-loader.js
index 790daf5a77ebd43b37fd8acac712ff6ff7a1efa3..f978e3339a9419d826b738140bcc3985893bbd49 100644
--- a/dist/shared/lib/image-loader.js
+++ b/dist/shared/lib/image-loader.js
@@ -9,16 +9,18 @@ Object.defineProperty(exports, "default", {
}
});
function defaultLoader(param) {
- let { config, src, width, quality } = param;
+ let { config, src, width, height, quality } = param;
if (process.env.NODE_ENV !== 'production') {
const missingValues = [];
// these should always be provided but make sure they are
if (!src) missingValues.push('src');
if (!width) missingValues.push('width');
+ if (!height) missingValues.push('height');
if (missingValues.length > 0) {
throw new Error("Next Image Optimization requires " + missingValues.join(', ') + " to be provided. Make sure you pass them as props to the `next/image` component. Received: " + JSON.stringify({
src,
width,
+ height,
quality
}));
}
@@ -53,7 +55,7 @@ function defaultLoader(param) {
}
}
}
- return config.path + "?url=" + encodeURIComponent(src) + "&w=" + width + "&q=" + (quality || 75) + (process.env.NEXT_DEPLOYMENT_ID ? "&dpl=" + process.env.NEXT_DEPLOYMENT_ID : '');
+ return config.path + "?url=" + encodeURIComponent(src) + "&w=" + width + "&h=" + height + "&q=" + (quality || 75) + (process.env.NEXT_DEPLOYMENT_ID ? "&dpl=" + process.env.NEXT_DEPLOYMENT_ID : '');
}
// We use this to determine if the import is the default loader
// or a custom loader defined by the user in next.config.js |
Beta Was this translation helpful? Give feedback.
-
Describe the feature you'd like to request
Images we load are often of slightly differing aspect ratios, i.e. 5:3 4:3 16:10 16:9.
This causes Lighthouse to complain about needing to serve properly sized images.
Our image loader service (imagekit) can 'cut' images to the requested aspect ratio.
i.e.
something.jpg?w=500,h=300
However,
next/image
is currently not passing theheight
parameter to the custom loader function.Describe the solution you'd like
Currently:
Wanted:
Add
height
toImageLoaderProps
.Describe alternatives you've considered
srcset
cuts are all wrong, as they all use the loader.next/image
Beta Was this translation helpful? Give feedback.
All reactions