Since version 10, Next.js comes with the built-in Image
component which provides the feature of image optimization and resizing image responsively. I like it a lot and I've been using it across my website for images with a fixed size. According to the official documentation, width and height are required props unless it's for layout=fill
.
Now, I would like to use Image
component even when I don't know width and height ahead of time. I have blog resources that come from a CMS where the exact size of the image is unknown. In this case, is there any way I could use Image
component?
Any help would be appreciated.
If you do not supply height and width explicitly the <img> element will be rendered at 0x0 until the browser can size it based on the file. When this happens it causes a visual reflow of the page once the image loads, and is compounded if you have multiple images on the page.
There are two ways you can display images in Next. js, you either use the conventional <img> tag or a specialized <Image/> component that is unique to Next. The differences between these tags are quite much, but they pretty much do the same thing; which is to display images to the browser.
These widths are used when the next/image component uses layout="responsive" or layout="fill" to ensure the correct image is served for user's device. If no configuration is provided, the default below is used. You can specify a list of image widths using the images.imageSizes property in your next.config.js file.
If you know the expected device widths of your users, you can specify a list of device width breakpoints using the deviceSizes property in next.config.js. These widths are used when the next/image component uses layout="responsive" or layout="fill" to ensure the correct image is served for user's device.
The component <Image /> requires some props like src, width, height. The <image /> component optionally accepts the following properties. <Image width= {500} height= {500} src= {imgSrc} alt="images name" />
Images load as they are scrolled into the viewport. The first step to use it is to import it from the Next.js library: And then itβs a matter of using it in your JSX code, passing it at least an src property: If you want to use images stored inside your Next.js project, an easy way is to put them inside the /public folder.
Yeah, you could use it with the layout=fill
option you mentioned.
On this case, you're gonna need to set an "aspect ratio" for your images
<div style={{ position: "relative", width: "100%", paddingBottom: "20%" }} >
<Image
alt="Image Alt"
src="/image.jpg"
layout="fill"
objectFit="contain" // Scale your image down to fit into the container
/>
</div>
You can use objectFit="cover"
too and it'll scale your image up to fill all the container.
The downside of this approach is that is gonna be attached to the width
you specify, it could be relative such as "100%" or absolute e.g. "10rem", but there's no way to set like an auto width and height based on the size of your image, or at least no yet.
π
I had the same issue too: I wanted to use Image
from next/image in a masonry ImageList
from MUI...
Anyway, this is the trick I used to get the size of my image and so to handle the size of the container:
import React, { FC, useState } from 'react';
import styled from 'styled-components';
import Image from 'next/image';
interface Props {
src: string;
}
export const MasonryItem: FC<Props> = ({ src }) => {
const [paddingTop, setPaddingTop] = useState('0');
return (
<Container style={{ paddingTop }}>
<Image
src={src}
layout="fill"
objectFit="contain"
onLoad={({ target }) => {
const { naturalWidth, naturalHeight } = target as HTMLImageElement;
setPaddingTop(`calc(100% / (${naturalWidth} / ${naturalHeight})`);
}}
/>
</Container>
);
};
const Container = styled.div`
position: relative;
`;
The idea: get size of image when it's loaded, calc the ratio and use it to make the container fill the require space with padding-top. This way no matter your image size the container will be fit to it.
Remark, I didn't use width or height on the container because I didn't need it with my project, but maybe you will have to set one of both for your project.
As I'm still beginning with React & NextJS & typescript, maybe the solution can be prettier, if someone have an idea to improve it I'll be pleased to read it! π
If you want to preserve the ratio of the original image you can do it like this:
<div
style={{
width: 150,
height: 75,
position: "relative",
}}>
<Image
src={"/images/logos/" + merger.companyLogoUrl}
alt={merger.company}
layout="fill"
objectFit="contain"
/>
The image will fill to be as big as the container allows. So if you know your images are wider than they are long, for example, you could just set the width on the container and then set the container height to be bigger than the predicted image height.
So, for example, if you're working with wide images you can set the container width to be 150px, then as long as the container height is larger than the image height the image will show up at the height of the original ratio.
I wrote a blog post, detailing how to get the size of remote images, while also using SSG. How to use Image component in Next.js with unknown width and height
import Image from "next/image";
import probe from "probe-image-size";
export const getStaticProps = async () => {
const images = [
{ url: "https://i.imgur.com/uCxsmmg.png" },
{ url: "https://i.imgur.com/r4IgKkX.jpeg" },
{ url: "https://i.imgur.com/dAyge0Y.jpeg" }
];
const imagesWithSizes = await Promise.all(
images.map(async (image) => {
const imageWithSize = image;
imageWithSize.size = await probe(image.url);
return imageWithSize;
})
);
return {
props: {
images: imagesWithSizes
}
};
};
//...
export default function IndexPage({ images }) {
return (
<>
{images?.map((image) => (
<Image
key={image.url}
src={image.url}
width={image.size.width}
height={image.size.height}
/>
))}
</>
);
}
nothing wasn't helped me from above variants, but i tried resolve issue another way, it works for me:
const [imageSize, setSmageSize] = useState({
width: 1,
height: 1
});
<Image
src={imgPath}
layout="responsive"
objectFit="contain"
alt={alt}
priority={true}
onLoadingComplete={target => {
setSmageSize({
width: target.naturalWidth,
height: target.naturalHeight
});
}}
width={imageSize.width}
height={imageSize.height}
/>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With