Web App Performance

Optimizing CSS, JavaScript, & Images

Maximiliano Firtman

Maximiliano Firtman

Independent Consultant
Web App Performance

Check out a free preview of the full Web App Performance course

The "Optimizing CSS, JavaScript, & Images" Lesson is part of the full, Web App Performance course featured in this preview video. Here's what you'd learn in this lesson:

Max wraps up the basic optimization recommendations by demonstrating how blocking CSS can reduce page performance, and JavaScript should be deferred as long as possible. A few tips for optimizing images and web fonts are also discussed in this lesson.


Transcript from the "Optimizing CSS, JavaScript, & Images" Lesson

>> Well, remember we mentioned CSS as appetizer. CSS should be small and as soon as possible. In fact, in our code, let's look at our code. Let me close these things I have here, index.html. I start with the script. I will solve the script problem in a minute, but that's wrong.

The first thing I need is a stylesheet. So I should move both stylesheets to the top, before anything else. So, I wanna help discovery, and I want the browser to discover my CSS file as soon as possible. Why? Because the CSS file will block render. I know that the browser will need all the CSS files to render anything on the screen.

So I don't wanna make a late discovery. I don't want the browser to discover this when it's late, okay? Make sense? Well, that's for CSS. Also, if I analyze what's going on here, I actually have three CSS files. Styles CSS, one that comes from Google, and background CSS.

And, you say, okay, why background CSS? Look at this. Why background CSS? It's this one. It's difficult to show you this without the hovering. This one, why is not downloading really fast as the other two? So if I have a style CSS here, it's starting the download process, it starts here.

But background CSS takes a while. I wanna push this back. Why? You see the red line, the vertical line. That vertical line doesn't happen before that CSS file. Because we know that the browser will never rend, the never rend the pixel until all the CSS files were downloaded and parsed.

And if I can move this somehow that way, maybe I can move that line as well. It's not about the line, it's about the metric that will improve user's perception, and that will improve conversion, okay? Remember that. So what's going on with our background CSS? Well, if we look at the code, the code is just adding styles.css.

But when we get into styles.css, it's importing the background CSS file. Because some designers prefer to do that to separate the CSS file, like in per topic or per section, but this is pretty bad for performance. Why? Because we have to wait until the browser downloaded and parsed.

Styles CSS file to actually realize that there is another one to download, so we have late discovery, the browser discover background CSS late. Does it make sense? So we don't want that late discovery for our CSS. So don't use @import. So here we can just copy and paste the CSS into one file or if you want on HTTP/2 is not going to have a lot of problems to add background CSS here, so we have early discovery.

Later today, we will see how to improve that in case you cannot do this. Say for some reason that CSS, the name of the CSS, we know that after some execution, well, we will see how to solve the problem. So now if I refresh, I should see a difference on loading CSS files that now background and styles are being downloaded in parallel, at the same time, not in sequence.

Can you see that? So then we are letting the browser know all the CSS as soon as possible. Make sense? We still have some problems with JavaScript, okay. But we will get there. Because JavaScript should be the dessert, as I mentioned before. So we need to defer JavaScript as soon as possible.

So because JavaScript will block parsing. And it also uses the main thread, the same main thread that is used by the browser to layout, composite, and make all the stuff. So first, remove or defer unused code. So I remember a research that found that typically 20% of the JavaScript file is not being used ever.

It's like orphan code that you have there in your JavaScript file that is not going to be used, but it needs to be parsed anyway. And JavaScript is the most expensive asset that you have in your website. It's not because of the file size, because maybe JPEG files can be larger.

It's because of the CPU cost, because we have to compile the JavaScript code. So there is a just in time compiler, user's devices and parse and compile the JavaScript and then execute that JavaScript code. And then that's typically 1000 times more costly than a JPEG. So that's why JavaScript can be so, so important.

And how to do that. So how to defer JavaScript code? Well, we need to convert our script tags into non-blocking script because by default when you have blocking scripts, you will see things like this, like a stair here in the diagram. Can you see that there is like a stair.

But you don't see any other parallel downloads. You just see one, one, one, one, and then parallel downloads. When you see this in your waterfall chart, that typically means you have blocking scripts. In this case, we have three scripts. The first one is HTML, and then script, script, script.

That's because we already mentioned this, but by default we have HTML parsing here going on. Here. The HTML is parsing. And it founds a script, and it says, there is a script. Okay, we do nothing else. We pause parsing. We pause everything else. We download the script and execute the script, and then we continue.

And that's wrong. We don't want that. That's why HTML5 supports now the defer attribute. If you use the defer attribute, it will download the script in parallel with the rest and it will wait for the HTML to end parsing to execute your JavaScript. That's defer. And then you have also async.

That async will download in parallel, but it will try to execute it immediately, as soon as possible, pausing the parsing for a while. If you don't know which one to use, you should use defer by default and let async only for one. It's a very specific case such as analytics and it should be really, really short because you're still harming performance of your parsing.

So in our example, we can go to the script, we don't need to move the script to the bottom of the body. That was an old trick, no need to do that. You just need to add the defer attribute. It's a Boolean attribute. And that's all. So now that JavaScript code is not going to block the parsing.

Have in mind, your JavaScript code won't be executed until the body ends parsing. Okay, so it will take more time to execute that code. By the way we are still on basics. These are basics of optimizing performance. Compress and Obfuscate JavaScript and CSS. This is still valid on HTTP2.

Compress is going through a compression tool and obfuscating the JavaScript and CSS. So it has been proven that if you do that, you may still improve the file size and transfer by around 10%. It's not mandatory. I mean, it's not one of the things that will change your life in performance, but every small thing counts.

I remember a joke from a web performance engineer saying that we as web performance engineers we are counting milliseconds as top models or counting grams, right? Like saying that we are counting milliseconds and for us milliseconds are really really important, okay? Release the main thread as soon as possible.

What's the main thread? The only thread where you're executing code by default. So you shouldn't start executing code, actually, you shouldn't take more than 15 milliseconds of the thread. Actually, that's known as a long task. If you have something, some JavaScript code that is taking more than 15 milliseconds and of course that depends on the CPU.

But if it's taking more than 15 milliseconds, it's converting into a long task. And that long task is probably pushing the rendering of the next frames. So you have kind of clunky user interface if that happens. Embrace responsive images. Responsive images means that you are providing to the user the image that it needs.

Not just a large image that then with CSS resize, or things like that, and we will get into that later. Embrace SVG, for icons, for logos, every time it's a vector-based image. It will be much better to send SVG. On 98% of the situation there are some cases, and remember, don't trust me.

Try it. Test that. Make two versions. Compare the versions. See the waterfall charts. See how the the web vitals are doing on each case and then make a decision. Compress images define placeholder size. Placeholder size is use CSS or HTML to define the width and the height of the image placeholder.

If you don't do that, and I think that's one of the problems we have in our website. we will have content layer shift. Because the browser will not know the size of the image before actually downloading the image. So it needs to download the image, decode the image to actually know, that image is 100 by 50.

And then it needs to lay out the rest of the elements around. Okay, make sense? And compress images is using tools that will compress your JPEG files, your PNG files to remove unnecessary data. Careful with web fonts. We are using web fonts. What's the problem with web fonts?

Web fonts are really nice. They can be really nice in your screen, but actually text by default is non blocking. When you have text in your HTML, your text will appear unless you use web fonts. When you use web fonts, and it depends how you're using web fonts.

By default you are converting something that is nonblocking into something that is blocking because now you won't see text until the font is downloaded and parsed. There are ways to solve that problem or to deal with that, okay? But for now let's say be careful. So those were just basic ideas on web optimization.

Do you have any questions on any of those?

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now