Image assets optimisation with Next.js

Luca Galeazzi
Cortilia Team Blog
Published in
4 min readJun 29, 2021

At Cortilia, we recently launched a brand new corporate website.

The main purpose was to create a place to share with consumers our ideals, ethics and sustainability goals behind our e-commerce cortilia.it.

While designing the new website, it was clear from the start that we wanted to make heavy use of images as part of our storytelling, so most pages were structured to include a big hero section, with a background that would support our copy content. Because almost the entirety of users navigations would end up including at least one of this hero components, those image will have to load almost immediately. This meant that optimisation on image loading times was key factor for this website.

In the past I used assets optimisation tools at build time, but was never completely satisfied with the performances and they also cause headaches when those assets needed to be updated, demanding a new deploy of the website.

So this time I was looking to optimise assets on demand, and was very pleased to discover that Next.js 10 ships with a built in Image component that does exactly that! Here’s a breakdown of the main features I was interested in:

  • On demand assets optimisation.
  • Works with a CDN, so that you can store assets outside of the app bundle.
  • Lazy loading and all the cool things you usually find in such tools.

Setup

Next Image component is super easy to set up and is very, very well documented, but the gist of it, is:

So a marketing website, with big images that fill up the entire viewport, that’s what the layout="fill" prop is for, you can then adjust positioning via css using object-fit and object-position. Priority is handy when you know that you’re gonna need that asset loaded right away, but usually it should always be off, and let the Image component work its magic with lazy loading.

Setting up your CDN link is also very easy and straight forward:

All reference of the configured domains in the Image component will instruct Next to pull the assets directly from your CDN. That’s it, is that easy!

Performances

But, is it? With this configuration you won’t fully achieve the potential of this component, you now have ultralight .webp assets but something is still not right, you would still be experiencing weird loading time, why is that?

See, Next Image relies on an in memory cache to serve optimised assets. Every time a new request is made, it measures the device viewport then loads the asset from the CDN, crops it on the fly by, stores it in the cache and serves it to the client. If an asset is already cached, it is served directly with no computation.

The catch is, that assets on the cache default to an expiration of just 60 seconds, and this is most definitely what is causing the longer than expected loading times on your assets.

Tweaking the cache

The key to have good performances with Image component is having a large cache of optimised assets and keep the computation to a minimum.

One way to optimise the cache is restricting device sizes of clients visiting your website, by default Next Image uses this breakpoints 640, 750, 828, 1080, 1200, 1920, 2048, 3840.

We used tailwind css with default breakpoints for our website, so it made sense to restrict the devices sizes to the same one used by the library:

So, now we cover more devices with the same assets, what about the assets being recomputed every 60 seconds?

For that, you’ll need to configure your CDN, in the same way a sensible Frontend developer would optimise any CDN, by setting a max-age header on your assets.

Next also supports the s-maxage header, if, for example, you have necessity to set different expirations for your assets that are only computed by Next Js. Next’s Image will look for s-maxage first, then look for max-age and only after that, fall back to 60 seconds.

We went back and forth a bit with the expirations and finally settled to 1 month, pick your own based on your feature release schedule and workflow.

With these simple tweaks we were able to reach good performances for first contentful paint and largest contentful page, measured with lighthouse:

Pitfalls

Since images are cropped on demand, having decent specs for your server it’s important, cropping is cpu intensive so if you have giant assets you need to process consider allocating more cpu cores.

While developing the site we discovered about a bug on Next Image in the lates versions of Next.js. Initially Next used an implementation based on sharp, a tool also used by many other cropping tools. Since v10.0.8 because of issues with sharp itself, the implementation was migrated to a custom implementation that is causing noticeable performance degradation, both in resource usage and computing time.

If you’re interested in using Next Image, my suggestion for now is to downgrade Next v10.0.7 and follow up the discussion about the fix [#23637].

Conclusion

Next.js is a really valid framework for React application the fact that it ships with an assets optimisation tool bundled in, is a big plus for marketing websites and e-commerces. Just remember to also configure your cloud environment and measure performance on both the browser, but also on your server.

Happy cropping!

--

--