Common image problems and introduction to next/image
If you're working with images on the web you might have noticed three common problems. The first is that there's layout shift from your images loading in, which is distracting for your visitors. Two is that you load images that are too large for your page so they don't get optimized for the viewport or for the device that the visitor is using. And the third is that the experience of actually getting the images on the screen and the layout that you want is actually pretty difficult to do. So you want to have a little bit better developer experience for using images well. The Image component in Next.js is hopefully here to solve those problems for you.
Basic local image import and automatic sizing
Okay, so in my editor I have the Next.js Image component examples from the documentation running locally and we're going to walk through a few different examples of using the Image component. So let's start with the most basic. I have this Image component defined on the left. It takes an alt tag for accessibility, some source image, and then I also passed in some styles to make it take up the full width of the screen. Now the source is coming from up top, or actually importing the image file from the public directory. Now this is nice because we don't have to manually set the width or height. Setting the width and height is important because this is how you prevent layout shift. So when you import an image here it's going to automatically do that for you. So now let's go look at the browser and see what's actually happening here. So on localhost:3000 I have dev tools opened here and I see an image tag. So there's no wrapping elements; it's just the native web image tag. In previous versions of Next.js we had to add wrapping elements to help support the features that were not cross-browser yet in terms of aspect ratios and styling, but now in the latest version of Next.js there's no wrapping elements. So we have an image tag, we see our alt tag, we see some properties that we've automatically defined for you like the width and the height to prevent that layout shift, and then we see this interesting URL. Now this URL is the automatically optimized image for us.
How next/image optimizes assets (AVIF/WebP) and file size examples
So let's take a quick detour here and I'll go look at this file slash pursell.png from the public directory that we're serving up. Now if we look down here we see that the original size of this is 30.3 kilobytes. This is a PNG file and I've done a hard reload here so it's not being cached. But this _next/image route is what's automatically optimizing that image and making it a bit smaller. So the same image passed in through that route we look here and we see that it detects that because I'm using Chrome we can actually use a more modern file extension. So we're going to use the format of AVIF or WebP, which are going to help you get even smaller file sizes. So we see that down below here with content type. And if I close this out we see that the size of this one is 7.2 kilobytes. So again I'll do a hard reload here and we see 7.2 kilobytes. That's quite a bit smaller, and you know 30 kilobytes was already a decently small image. This gets even more impactful when you talk about megabyte-sized images or really high-quality images.
Remote images: allowed domains, patterns, and required dimensions
But what if my images aren't in my repository? They're from some remote location. Well, that's okay too. Let's take a look at another example. So in my left I have another Image component with an alt tag and a source and a width and a height and the max width, but notice that the source is a URL that is some external image. Now this also works inside of Next.js, but we have to define an explicit allow list of the domains that we want to be able to optimize images from. So if we hop over to our next.config we see that not only have we defined the image formats that we want to support, which are AVIF and WebP, we also can define some patterns that we want to match for the URLs inside of our source. So we're saying we're going to allow all of the images from this assets.pursell.com subdomain and specifically for this pathname image slash upload slash something. Now this is important because it makes sure that you lock down the URLs that people can use your Next.js server to optimize images with. It's important to note here that we still had to define the width and the height of the image. Now this can be a little bit tricky sometimes if the width or the height are really dynamic, but it's really important because this is the only way that you can go without having layout shift from your image loading in.
Background images with fill and sizes
Okay, let's look at another example of setting a background image for the entire page. So on my left I have another image tag. It has an alt and a source just like before, but there's a couple new things here we want to dig into. The first I'm going to talk about which is the most important is fill. So rather than setting a width and height of this image we wanted to take up the entire width of our parent and we can do that by specifying this fill prop. Now previously in older versions of Next.js there was a layout prop that had a few different options, but layout has been replaced with different options including this fill prop, so go check out the documentation if you want to see those new options. And secondly we define this sizes prop, so we're telling Next.js — and I'll actually pull up these here because there's quite a bit of details here on sizes — but it's really important that if you're using fill you should also use sizes. The most important part here is if the sizes property includes something like 50vw or 100vw in this instance, which is a percentage of the viewport width, the source set or the number of images is trimmed down to not include any values which are too small to ever be necessary. So you get a way to configure the different variations of the image that you want Next.js to automatically serve up depending on the viewport and the device of your visitor. Now there's a couple other cool things here. One, there's this optional quality prop. It defaults to 75 percent, but you can optionally do 100 if you would like. It's going to be a slightly bigger image, but for a background image that might be nice to have. You can have a nice blur-up placeholder which is really easy to do when you're importing a local image; that local image Next.js is going to be able to automatically generate the blurDataURL which gives you that nice blur-up placeholder and has a better UX. And the last thing here is a style prop, so this is just using CSS. You could do this through a className if you wanted to too, but we have object-fit: cover. We could also do something like contain. I think none is another one. You could have a couple different options here on how you want to actually lay this out, but I typically use cover for most of these things.
Using multiple images in a CSS grid with fill
Let's look at another example of using multiple images with layout fill in a grid pattern. So on the left this image looks pretty similar to the last one. It has fill, it has some sizes, and then I'm also using object-fit: cover, but you'll notice that I've specified a relative parent around it which is very important and a height. Now the parents of this element is a CSS Grid; it's got a little bit of spacing between the items. We've got some CSS magic of auto-fitting the sizes. So what happens here if I take this element of the mountain image and we add some more images to the grid — so let's do maybe six images here — so now what you'll see is that these images are going to have this relative position of their wrapping component and it's going to have a height of 400 pixels. And this value — let me just resize here a little bit — you get this really nice effect from CSS Grid where the combination of object-fit: cover means that this scales really well and it can be responsive thanks to CSS Grid as well too. So this is a really nice pattern that I like to use.
Responsive images, API knobs, and custom loaders
Okay, the last one I want to talk about is responsive images, which the great thing about the Image component is that they're responsive by default. So on the left again I have an alt tag, I have a source, sizes, and then I'm making the image display full width, but now what's really cool is that by default when I resize the viewport the Image component is automatically responsive. So that's really, really nice. And because I have a flex parent here I could also do something like, let's say I want — hello world, another item — I can display that down below and I could have some kind of card layout, for example, and I can just use this really, really nicely. Now if you want to fine-tune or tweak any of the knobs on the Image component we have a full API reference in our documentation including the ability to change what service actually optimizes your images through loaders. So by default, and it works whether you self-host or you use a managed service, you can optimize images on the Next.js server. Now if you, for example, want to use a different service to optimize your images you can specify a loader that will make this very easy for you. So if you look at loader it takes in a function; this can define the URL for any service and you can pass in things like the source, the width, and the quality, and it makes it really easy to swap out for whichever service you prefer.
Closing and call to action
Okay, that's all for this video. Hopefully you found this helpful to see some practical examples of how to use the Image component. Stay tuned for the next one and let me know in the comments what you'd like to see. Peace.