We're currently displaying product images
using a standard HTML "img" element.
But as I mentioned at the end of the last video
this way the images are downloaded
at their original size,
that is larger than what
we're displaying on this page.
Also, all the images will be loaded
as soon as we load the page,
even though some of them are not visible,
because they're "below the fold",
meaning we'd need to scroll down to see them.
Thankfully Next.js comes with some nifty features
around "Image Optimization".
It provides a component called Next Image,
and some functionality built into
the Next.js server,
so that it can automatically resize images
to match the dimensions we use in our pages.
It can convert those images
to modern formats like WebP,
that take up less space compared to
older formats like JPEG.
Finally it allows us to lazy load our images,
which means loading only those that are
currently visible on the screen.
Now, the way the Next.js server does all this
is "on-demand".
Which means that for example
if an image needs to be resized
that will happen the first time
we request that image from the server.
So this doesn't happen at build time.
Next.js will not statically generate
all possible images when we build the app.
But as we'll see it will cache the generated images
after they've been resized on-demand.
Anyway, enough of introductions
and let's see how it works in code.
The central piece of the puzzle
is the "Image" component
exported by the "next/image" module.
We can use this component simply as a replacement
for the standard HTML "img" element.
But typically we also need to specify
the dimensions we want.
Let's set the "width" to 320
and the "height" to "240", as numbers.
This way we can let the Next.js server know
how it should resize these images.
If we save now,
we actually get an error.
It says "Invalid src prop"
"hostname localhost is not configured".
And it includes a link where we can find more info,
so let's read this documentation.
Here it basically says that
we need to specify from which servers
we want to be able to fetch images,
adding them to a configuration file
called "next.config.js".
So this is essentially a security restriction.
You don't want your Next.js server
to fetch images from anywhere on the internet,
because that could be abused.
You only want to allow images from one
or maybe a few predefined locations.
Now, we should already have that configuration file
called "next.config.js".
It was generated by Create Next App
when we first set up this project.
So let's copy the bit about "images" from here,
and add it to our "next.config.js".
But we need to change the domain.
In our case we're fetching images
from our Strapi server that's running on "localhost".
We actually have both the Next.js server
and the Strapi server running on "localhost",
but on different ports.
Note that "localhost" here refers
to the Strapi server,
that's the source for the images.
Anyway, if we save this file and reload the app
it still doesn't work.
That's because we need to restart the dev server.
The configuration file is not automatically reloaded.
And now it's working again.
So it is now using the Next Image component
to load the images.
There's no obvious difference at first sight,
the images still look the same.
But if we look a bit deeper,
with the help of the Chrome Developer Tools,
we can see that for a start
the images are now being loaded
from the Next.js server,
and not directly from the CMS.
We can tell because the URL has port 3000.
And it's from a special path
that starts with "_next/image",
and then the original image URL.
That's what allows the Next.js server
to act as a sort of proxy.
If we look at the dimensions
you can see that this time the image is
640 by 480 pixels,
which is smaller than the original image
stored in the CMS, that was 960 by 720.
Interestingly it's still twice as large as
the dimensions we specified, that were 320 by 240.
This is probably because I have a Retina display
so it's using double resolution.
But in any case, the image was
definitely resized by the Next.js server.
If we look at the network requests
we can see some other interesting things.
Let me reload the page,
so it will re-fetch everything.
One thing to notice is that
the images are in "webp" format.
If you remember, the image in the CMS was a JPEG.
So the Next.js server converted it to WebP.
The other interesting thing is
that it only loaded 3 images so far,
even though we know there are
more than 3 products in our HomePage.
That's because the Next Image component
uses lazy loading,
so it didn't load the images that are
not yet visible on the page.
If we scroll down, you can see that at some point
it loads a fourth image,
and as we keep scrolling,
more images come into view
and therefore will be loaded.
That's a great optimization technique,
especially on a page such as this one
where we have lots of product images,
but only a few of them will be initially visible.
Ok. So that's another nifty little feature
we get for very little effort in our app.
The final thing I want to show you is that
the Next.js server will cache the generated images
somewhere under the ".next" folder.
So when the same image is requested again
the server will simply return the cached file.
It's not something we need to worry about
because Next.js does all that automatically,
but it's good to know.
So this how we can take advantage
of all the image optimization features
provided by Next.js.
Simply use the Next Image component
instead of the standard HTML element
whenever we display a picture,
and Next.js takes care of all the rest for us.