June 30, 2025
Lighthouse - Optimize your website, Part 5: Performance
It's been a long time since the last blog post because I've had a lot of other things to look into, so I haven't had the time to finish the Lighthouse series. And I don't want to do something without a certain amount of quality. But now I'm back - and I've moved the the blog to a headless setup using Umbraco as the CMS behind the scenes. The site is build using Nuxt and I'm hosting it at NuxtHub. I'll probably do some posts about that in the future.
But now to the topic of this post: Performance!
As I've mentioned in the previous posts all parts of the Lighthouse report is important. Fixing one of them isn't enough to have a well performing website. As usual I've created a test page for this article: https://test.frontenddesigner.dk/performance_low.html. As you can see my test page really doesn't perform. Have a look at the results here:

Even though the score is pretty bad, the page actually passes a lot of audits. When just working with a static html page you actually have to make an effort not passing a lot of these audits, so I'll dig deeper into some of them even though the test page passes the audits. As you can see in the screenshots with the scores from my page Google tells us that:
Later this year, insights will replace performance audits. Learn more and provide feedback here.
I don't know exactly what impact this has on the performance part, so I'll only be looking at fixing the issues I actually have as this entire article may be up for a rewrite later this year. I'll keep you up to date when I have had some time to look into it.
Let's have a look at how we can fix the issues I have on my page:
Avoid large layout shifts
Properly size images
Serve images in next-gen formats
Image elements do not have explicit width and height
Serve static assets with an efficient cache policy
Avoid enormous network payloads
Largest Contentful Paint (LCP) element
Eliminate render-blocking resources
Avoid serving legacy JavaScript to modern browsers
Reduce unused JavaScript
Avoid large layout shifts
A layout shift occurs when an element on your page somehow changes position or size resulting in affecting the positions of content around it. An unintended layout shift is bad for the user experience and that's why it's important to take care of the layout shift. Intended layout shifts caused by user interactions is no problem, but we need to fix the unintended shifts.
In my case I've got three images with no width and height attribute or an aspect-ratio attribute telling the browser which space the images is supposed to use on the page. Let's start by adding and aspect-ratio property to the images using CSS. I'm just going to put them inline on the images as I haven't got a css file for the page.
<img src="https://test.frontenddesigner.dk/daniel-lorentzen-0FNc6GOceCk-unsplash.jpg" style="display: block; aspect-ratio: 1200 / 1800" alt="" />
<img src="https://test.frontenddesigner.dk/javier-rincon-9Kbh8hMWVAc-unsplash.jpg" style="display: block; aspect-ratio: 1200 / 800" alt="" />
<img src="https://test.frontenddesigner.dk/free-nomad-uQ3jKHjnPHs-unsplash.jpg" style="display: block; aspect-ratio: 1200 / 1675" alt="" />
As you probably noticed I also added "display: block;" to the images. I need this (or inline-block, flex, inline-flex etc.) to make the aspect-ratio have any effect.
Properly size images
The images I use on my test page is pretty large and they are only showed in a 1200 px wide container. This is why the page doesn't pass this audit. So let's just save the images in the appropriate size for the page. I've created the same images with a "_1200" postfix to the name. No real changes to the markup is made - except from changing the source of the images:
<img src="https://test.frontenddesigner.dk/daniel-lorentzen-0FNc6GOceCk-unsplash_1200.jpg" style="display: block; aspect-ratio: 1200 / 1800" alt="" />
<img src="https://test.frontenddesigner.dk/javier-rincon-9Kbh8hMWVAc-unsplash_1200.jpg" style="display: block; aspect-ratio: 1200 / 800" alt="" />
<img src="https://test.frontenddesigner.dk/free-nomad-uQ3jKHjnPHs-unsplash_1200.jpg" style="display: block; aspect-ratio: 1200 / 1675" alt="" />
Serve images in next-gen formats
My images are in the good old jpg format, but new and better formats are ready for us. That's why the page doesn't pass the next-gen image formats audit. Let's convert them to webp (I used https://cloudconvert.com/jpg-to-webp to convert them). Let's change the src of the images to the new webp images:
<img src="https://test.frontenddesigner.dk/daniel-lorentzen-0FNc6GOceCk-unsplash_1200.webp" style="display: block; aspect-ratio: 1200 / 1800" alt="" />
<img src="https://test.frontenddesigner.dk/javier-rincon-9Kbh8hMWVAc-unsplash_1200.webp" style="display: block; aspect-ratio: 1200 / 800" alt="" />
<img src="https://test.frontenddesigner.dk/free-nomad-uQ3jKHjnPHs-unsplash_1200.webp" style="display: block; aspect-ratio: 1200 / 1675" alt="" />
The server where I host my test site didn't support the webp image format by default so I had to add the following to the web.config file:
<configuration>
<system.webServer>
<staticContent>
<mimeMap fileExtension=".webp" mimeType="image/webp" />
</staticContent>
</system.webServer>
</configuration>
Image elements do not have explicit width and height
This is a quite easy fix. We just need to add the width and height attributes to the images on the page. As we've already added the aspect-ratio css property and the images has the size of the container they're placed inside, these two changes won't have any visual effect to the page.
<img src="https://test.frontenddesigner.dk/daniel-lorentzen-0FNc6GOceCk-unsplash_1200.webp" width="1200" height="1800" style="display: block; aspect-ratio: 1200 / 1800" alt="" />
<img src="https://test.frontenddesigner.dk/javier-rincon-9Kbh8hMWVAc-unsplash_1200.webp" width="1200" height="800" style="display: block; aspect-ratio: 1200 / 800" alt="" />
<img src="https://test.frontenddesigner.dk/free-nomad-uQ3jKHjnPHs-unsplash_1200.webp" width="1200" height="1675" style="display: block; aspect-ratio: 1200 / 1675" alt="" />
Serve static assets with an efficient cache policy
My webp images and my CSS and JS files are only cached for 4 hours. That's not enough to make Lighthouse happy. I don't want to cache everything in my root folder, so I created an "assets" folder on my test site and moved the webp images, my CSS file and my JS file inside the new folder. To cache the files I added the following to my web.config file:
<configuration>
<location path="assets">
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="365.00:00:00" />
</staticContent>
<httpProtocol>
<customHeaders>
<add name="Cache-Control" value="public" />
</customHeaders>
</httpProtocol>
</system.webServer>
</location>
</configuration>
If you're working with a CMS their might be different way defining the cache with settings inside the CMS. This was, however, the quick and easy solution for me on my simple test page running on an ASP.NET environment and no CMS or server access.
Avoid enormous network payloads
This issue was actually fixed just saving our images as jpgs in the right size for the page. After this we did further improvements by converting the images to webp and caching them on the server. So we wont have any issues here with the fixes above.
Largest Contentful Paint (LCP) element
As with the "Avoid enormous network payloads" this issue was fixed by optimizing our images by saving them in the right size for the page, converting them into the webp format and caching them on the server.
Eliminate render-blocking resources
The last issue on the page is that it contains a render-blocking ressource. When a ressource is render-blocking it means that it's blocking the first paint of the page. You can read more about render blocking ressources here: https://developer.chrome.com/docs/lighthouse/performance/render-blocking-resources/. As they write:
The goal is to reduce the impact of these render-blocking URLs by inlining critical resources, deferring non-critical resources, and removing anything unused.
I haven't fixed it on my test page (I'll get in to why later), but my score is still pretty good on my new and improved page: https://test.frontenddesigner.dk/performance_high.html
Avoid serving legacy JavaScript to modern browsers
In this part Lighthouse tells me that the following legacy js is served to the browser:
@babel/plugin-transform-classes
@babel/plugin-transform-regenerator
I don't know exactly where this comes from, but I can get a hint using the following command in my terminal to see the dependency tree of my npm modules:
npm ls --all
In this case I don't see the @babel/plugin-transform-classes or @babel/plugin-transform-regenerator but I do see some other babel related modules. All of them is related to Vue so this might indicate that there's some reference to these modules inside Vue somewhere. I'm a big fan of Vue and use it in almost all of my solutions, but this is a great example that you have to keep updated when using third party resources. I'm not going to dig deeper into this as this could require a lot of time consuming debugging and testing. It might be fixed by just securing that we use the latest version of Vue and it might not.
If you did not use Vue and build everything with Vanilla JS you'd end up having a huge amount of JS - and you might end up having the same problem with your own JS. So this is not only related to Vue (or any other framework). It's simply just related to having to stay up to date đ
Reduce unused JavaScript
I used a JavaScript file from our Baseline at my job for this test page. In the markup I only use a accordions that require some JavaScript but the file also contains JS for tabs, navigation, search, videos etc. When I remove the parts I do not need and only include the accordion part, we do not get this error anymore.

The reason I didn't fix my render-blocking ressource was that it was a CSS ressource. In my job as a developer I develop a lot of solutions - and they change a lot over time. I'm not in control of any on the content on all of the solutions I develop - and all of them are build on CMS platforms where we usually make them very generic - meaning that any "module" or component could be the first component on a page. This makes it almost impossible for us to determine what the critical CSS is for the entire solution - and Google (still) suggest putting the critical CSS inline on the page and then defer loading the rest of the CSS.
I've actually done this many years ago with different tools automating the process, but we've decided (where I work) that this is not worth the trouble because of the solutions we deliver, where we can never be sure what our customers put as the first content on their pages.
If I only worked on internal solutions I would recommend creating the needed critical css, inlining it and defer loading of the rest. But as for now, it doesn't really makes sense for me on the solutions I work on. You can learn more about the approach here: https://developer.chrome.com/docs/lighthouse/performance/render-blocking-resources/#how_to_eliminate_render-blocking_stylesheets
Conclusion
The most of the corrections we did to fix our issues was related to the large images I had on the page. What we did was:
Adding aspect-ratio CSS to our images
Saved our images in the correct sizes for the page
Converted our images to webp format
Added with and heigh attributes to our images
Made sure our images was cached by adding a bit of code to our web.config file
Removed unused JavaScript from my JavaScript file. I only used the accordion part of the script, so everything else was removed
What we didn't do:
We didn't do any inlining of critical CSS for our page as this doesn't necessarily make sense to do. It depends a lot of who you are developing for. If you're developing for yourself or the company you work for in terms of internal sites - go ahead and do it. You'll probably end up getting 100 / 100 đ But if you're working for clients who'll be able to change the content on the site in many different ways, I wouldn't recommend doing anything about this.
Disclaimer
My test page did already pass a lot of tests because it actually requires a lot to make a simple and static HTML file perform really bad in all of the audits. I did plan to to take a look at some of the audits I passed, but as mentioned in the beginning Google are about to replace the performance part with Insights, so I'd rather wait and make an updated and relevant article later on. It makes a lot more sense than making a lot of examples that might be outdated later this year. I'll keep you updated.
If youâve got any questions regarding this article â or others â please donât hesitate to reach out at [email protected]. Youâre also welcome to reach out if you have further tips & tricks you see missing in my articles. Iâll update them and credit you.