Preliminarily, it does seem like the CDA isnāt correctly taking into account some of the Imgix transformations that can affect size: like trim
, yes, but also pad
or rot
and maybe others. The devs will look into it and Iāll provide updates as I can.
In the meantime, if this is an urgent need, you can either provide the correct dimensions manually, like:
<Image data={{...photo.responsiveImage, height: 102}} />
Or since youāre using the clientside <Image/>
component, you can kinda hack around it by letting JS load the image in the background and getting its real dimensions:
Example code:
import "./App.css";
import { Image as DatoImage, type ResponsiveImageType } from "react-datocms";
import { useEffect, useState } from "react";
const App = () => {
const mockCdaResponse: ResponsiveImageType = {
src: "https://www.datocms-assets.com/160930/1747248478-test.png?trim=color",
width: 200, // Assuming you specified an explicit `w:` parameter
height: 100, // Bug: This comes back as a function of the original aspect ratio, EVEN if you set an explicit `h` parameter
};
// We have to handle this asynchronously because image loading/rendering is inherently async
const [realDimensions, setRealDimensions] = useState<{
width: number;
height: number;
}>();
// So we use an useEffect to essentially "preload" the image in JS and use that hidden render to calculate the dimensions
useEffect(() => {
if (!mockCdaResponse.src) {
return;
}
(async () => {
try {
// getImageDimensions() is defined at the bottom. Helper func that loads an image and calculates its size
const dimensions = await getImageDimensions(mockCdaResponse.src);
if (dimensions?.width && dimensions?.height) {
setRealDimensions(dimensions);
}
} catch (error) {
console.error("Error", error);
setRealDimensions(undefined);
}
})();
}, [mockCdaResponse.src]);
return (
<>
<h1>DatoCMS Trim() Image Test</h1>
<h2>Trimmed (default CDA response)</h2>
<DatoImage data={mockCdaResponse} />
<h2>Trimmed (using calculated dimensions)</h2>
<DatoImage data={{ ...mockCdaResponse, ...realDimensions }} />
</>
);
};
export default App;
/**
* Asynchronously retrieves the intrinsic dimensions of an image given its URL.
*
* This function creates a new HTMLImageElement and sets its `src` attribute to the provided URL.
* It waits for the image to load (or fail to load), then returns the image's natural width and height.
*
* The loading process uses the browser's native image-fetching mechanism, which benefits from
* built-in caching. The image does not need to be added to the DOM and is never displayed.
*
* @param url - The URL of the image to measure. Must be a non-empty, valid URL string.
* @returns A Promise that resolves to an object containing the `width` and `height` of the image in pixels.
* @throws If the URL is invalid, empty, or the image fails to load (e.g. due to network error or CORS).
*
* @example
* ```ts
* const { width, height } = await getImageDimensions('https://example.com/photo.jpg');
* console.log(`Image is ${width}x${height}`);
* ```
*/
const getImageDimensions = async (
url?: string | null,
): Promise<{ width: number; height: number }> => {
// Validate input URL
if (!url) {
throw new Error('Image URL must be a non-empty string.');
}
try {
new URL(url); // Throws if not a valid URL
} catch {
throw new Error(`Invalid image URL: "${url}"`);
}
const img = new Image();
// Wait for the image to load or fail
await new Promise<void>((resolve, reject) => {
img.onload = () => resolve();
img.onerror = () => reject(new Error("Failed to load image."));
img.src = url;
});
return { width: img.width, height: img.height };
};
Itās hacky, of course, but might be better than distorted images in a pinchā¦?
Sorry about that. Iāll let you know once the underlying issue is resolved (if possible).