Image Optimization - Quick Win for Improving Performance in Vue & Nuxt Apps


Improving Performance of Vue or Nuxt apps is not an easy task. However, there are some quick wins that you can implement easily that will drastically improve the performance of your website. One of such quick win is Image Optimization. In this talk, I will teach you how to optimize images on the fly with Digital Asset Managements services like Cloudinary or Content Management Systems, utilize the concept of Lazy Loading, use compressed format of images like WEBP, and many more!


Okay, do you hear me well? Yeah? Good. Okay. So you might be thinking that in order to improve performance of the website, we need to focus on the backend stuff, right? So we need to optimize or make the SQL queries more efficient. We need to go for the cache implementations and stuff like that. But in reality, there are actually several things we can do on the front end itself to make it more performant. And this inspired me to basically take the topic of image optimization for today's presentation as it is, in my opinion, one of the quick wins for improving performance in your website. So as Dom already introduced me, my name is Jakub and I work at vue Storefront. And I'm also a Google developer expert in web performance. This is also one of the reasons why I selected the topic. And I'm also an ambassador in those organizations. So if you're interested in talking about any of them, just find me after the talk and we can just share the knowledge. And yeah, talk about it. And as Dom also mentioned, we met two years, almost two years ago on the last edition of vue.js Live. So I'm pretty happy to be here once again and share my knowledge with all of you. So I will start the presentation with a little poll. So please raise your hand if you are using images in your website. That's basically really straightforward, right? It's yes, we are using the images in our websites. And now, please raise your hand if you are optimizing the images. Okay, so quite a few of you, but not so many. So you can clearly see that we are all using the images, but we are not optimizing them. While optimizing images is, in my opinion, the crucial part of well-performing websites. And it's not only about the results of the Lighthouse or any other web performance auditing tool. It's also good for user experience because users want to get the data as soon as possible. Like the images, if you are looking for e-commerce website, you want to have the product image as soon as possible because you want to buy the product. So let's talk about first about the image formats. So there are many technical differences between one image format and another. And in my today's talk, I wanted to focus on the optimization. So basically, the size and the load time of the image. So I want to focus only on one. And yes, be prepared, there will be some more memes as well. For the users, the usual difference between one image, which is, let's say, in one format and the other image, which is in different format, is usually the same. You can't see the difference. They just look the same. The only difference is the size. And this is what I will be showing during the short demo later on. But in general, formats such as WebP or Aviv, I hope I pronounced it correctly, are lighter than JPEG and PNG. But they are not supported by Internet Explorer. And I'm saying this because one of my friends, until today, is supporting application that still needs to be running on Internet Explorer. So he doesn't have a great job, I would say. So we talked about the image formats. There are also cases about the size, properly sized images. And I have seen too many production websites where they were basically fetching the image, for example, for a small icon. And the image was in a full HD resolution. And then it was squeezed into the icon that was, for example, in the footer. So this might seem like a really, really strange issue. But it happens too often, I would say, for the modern websites. So why should we have the images in a proper size? It's basically because the load time, if we are fetching the images in the right format, the load time will be shorter. And there will be also what is the second most important thing. We won't see the layout shift. Because you will have, if you've seen some talks or articles about the web performance, you probably know already about the core web vitals. This is like the metric or set of metrics that we can use in order to check how our website is performing. So there are metrics such as first, largest contentful paint and cumulative layout shift. And having the properly sized images is important for both of them. But we can optimize those images by basically replacing the or converting the one of the images format into another one. We can just have the image in correct resolution from the very beginning. But in majority of cases, we want to be able to optimize them on the fly by using certain software. So for that, we can use optimizing image, optimizing services. I'm not sure if there is a word such as this one or a keyword. I just made it up. So basically, the idea behind the services is to allow us to optimize the images on the fly. Whenever we need it and with the parameters that we need as well. So we can use the tool called IPX, which is the tool developed by the Next Team as well under the NGS organization. And the NGS organization in general, if you don't know it yet, it's an organization where there are packages that are used in many different frameworks because they are javascript slash typescript only. So you can use it with vue, react. So IPX is the image optimizer, which is based on Sharp and Vips. And it's really straightforward, the usage of it. We can just install it and we can use it as the Express.js middleware or we can use it as just the IPX client. And how we can use it later on in our application, we can just send the request to the local host with certain parameters. For example, the format. In this case, it will be WebP. But the IPX works well and actually it's used in the Next Image, the module that is widely used in the Next ecosystem for handling images. However, there might be cases when we basically cannot use such a thing as IPX. We just want to have the third-party service handle this case for us. And for that, we can use a tool such as Cloudinary. So Cloudinary is the digital asset management that allows you to manage your assets of your website. So photos, pictures, videos, GIFs, many things like that. And then what we can do with it is we can fetch them in a way we want. So in the right format, in the right size, with many different optimizations applied to which I will show in a minute. So in Next World, we can use Cloudinary with Next Image because Next Image supports 18 providers and Cloudinary is one of them. But we can also use the Cloudinary module, which is specifically for working with Cloudinary. That was developed by Maya Chavin. Maya Chavin is also popular in the vue community. And she was the original creator of this module. And yeah, so you might be asking yourself, what is the reason or basically what is the difference between using Next Image and the Next Cloudinary if they both can integrate with Cloudinary? So the idea behind Next Image is to be generic. So no matter what provider you will use, you can still use it with one api for the image. However, for the Next Cloudinary, the idea was to deliver more functionalities from Cloudinary, more optimizations, more transformations, image transformations. And there is also one more difference in the Next Cloudinary is that the module itself was developed some time ago. And it works only for Next 2, which as you know right now, Next is transitioning into Next 3. So I have a small surprise for you. And I was joining forces with Colby, Colby Fajok is the Director of developer experience at Cloudinary. So we have joined forces recently to release a Cloudinary module for Next 3. And I will show you that in a minute. So let me just go to the code. So the usage of the Cloudinary module is really simple. We just, as with many different modules as well, we just install the module, then we register it in the modules array. And then basically, we just need to add the cloud name, the specific cloud name that is assigned to our account. And then what we can do is we can start using the CLD image, which is the component specifically created for working with Cloudinary. So as a result, what we have is that when we go to the localhost, this is the image that is fetched from Cloudinary. And if we inspect, by the way, is the font OK, visible? OK. So as you can see, the type of the image is aviv. Because Cloudinary, by default, what the URL loader of Cloudinary will do is it will automatically try to use the most efficient image format. So in this case, it will be aviv. So take a look at the size. It says 22.3 kilobytes for this image. And as you can see here, the SRC of the image is JPEG. So we already see the transformation being done. It was JPEG transformed into aviv. So let's try to make it like a WebP. So here you see 22 kilobytes. And when I refresh, you see that changing to WebP increased the size by 15 kilobytes, more or less. So let's make it an original format, which will be JPEG, if I can type it correctly. So 33 kilobytes. Now it's 55 for the original one. So we already managed to get half of the size of the image by just using this format. And now let's go for PNG, just to show you the difference. So with PNG, PNG is 200 kilobytes. So basically 10 times bigger than the image in the aviv format. But Cloudinary doesn't only allow you to change the format. It comes with many more functionalities out of the box. So let's say that instead of just changing the format, what I would like to do here is add some overlay to our image. So it will have some text, some styling as well. So when I uncomment it and just make it a little bit smaller and refresh the page, you see what more can be added to the image. And this is being done fully by Cloudinary. And we can inspect that by checking the source of the image. You see here the full source of the image. So basically everything that we defined here in the code, the overlay, is being added as the parameters for Cloudinary to optimize the images for us. So yeah, the version 2.0 for the Cloudinary module for NUX is not yet released. But it will be in the next week. So with the rising popularity of NUX-free projects, you will be able to use this module in the upcoming days as well. So let me come back to the slides, because I have a few more patterns that I wanted to share with you. And the first pattern that I always like to recommend and use in web applications is basically lazy loading, and specifically lazy loading of the images. So I can explain to you how it works. But I will show you a GIF with some cuts that will show you basically how it works. So we have the cuts, images of the cuts. And you can see in the requests queue that they are fetched basically when the user is scrolling down, so when they are actually needed. So instead of fetching all of them at the beginning, at the initial load of application, we can just defer them to the point that the user will see them in the viewport. And it can be done very easily. You can use the loading attribute of the image. You can also use packages such as Lozat or the intersection observer. But one small thing that I noticed recently in the Mozilla docs that lazy loading or lazy value for the loading attribute of the image is explained as that the image will be loaded when necessary. So this is not entirely exactly true, because, well, it is true, but it doesn't give you the full context of the lazy loading. Because what I noticed in many web applications like production, and I will talk about it in a second, is that there are a majority of web pages, or for example, e-commerce websites, that are lazy loading the largest contentful paint. So to give you more context, largest contentful paint should be delivered as soon as possible. And when we are using lazy loading, we are adding the request for this image to the end of the request loop, which basically makes it load longer. So can you also see the difference in the size of the images that are running on the website that is using lazy loading and not using, basically as shown on the GIF with the cats? We can use it to defer loading the or requesting the images until the point they are actually needed. Lazy loading is part of what I call the lazy pattern, and basically I call it like that. I'm not sure if this is a pattern. And I'm really a big fan of this pattern. This is me with two of my cats, the last lazy Sunday. So I'm a huge fan of it, and the idea behind having such a lazy pattern is to use the techniques that are related to deferring doing some functionality until it's actually needed. This is like, I made this definition. So you can read about this lazy pattern in my article about improving performance of NUX with lazy pattern. And as a part of this lazy pattern, what it comes with is, of course, the lazy loading, as I mentioned, components dynamic lazy imports, lazy data fetching composables, and lazy hydration as well. Lazy hydration is a great package by Harlan, who is also a core team member of NUX. So you should check that out. What's next? There is also a fetch priority, which is an experimental feature that allows you to instruct the browser when it should fetch the request. So you can basically use it to tell the browser, okay, this one is super needed, and it should be loaded as soon as possible. So we can just say for the, let's say, the banner, home banner of our home page, we should use fetch priority to allow it to load as fast as possible. And we can do it very easily with the image tag. Just set fetch priority to high for the images that we want to fetch as soon as possible, and we can also instruct the browser for certain images to be fetched later on. So this is just the tip of the iceberg in improving performance, because there is a lot of things that we should take a look at. And I'm sure right after me will be Filip, who will be showing you some insights into web performance as well. And if you are interested more in learning more about this topic, so I have this series on DevTool about NUX performance, so NUX specific. And also recently I started writing about the web performance. So the usage of IPX, which I mentioned previously, the web front-end web performance checklist, so things you should take a look at while trying to make your website more performant. And I have small final request to you to end my talk. By using optimized images, you deliver, you basically deliver better experience to the users. This is cool, because users like to be entertained, they like to get the cool experience. So who doesn't want to be cool, right? Okay, thank you very much for being here and listening to me. If you'd like to find me somewhere on the internet, you can search for Jakub Androwski. It's easier to pronounce my last name in this way than in the original one. With a small difference in GitHub, I'm Barosz. Yeah, thanks. Thanks a lot Jakub, that was awesome. Always love your ideas about web performance. Feel free. Okay, I need to take out my phone, but before that I actually have one question. A couple of years ago in one project, we had a special component for loading images, and we always had two different images as assets. One was full resolution and other resolution was, I don't know, something like 50x, 50 pixels or 100x100, which was extremely small. And then we always loaded the first one, showed that one, which was like just blurry. And then when the larger one was fetched, we obviously replaced it with a better quality. What do you think about that technique? So it's a good idea in general, and it's also part of something what is called the progressive loading. So if you'd like to check this concept out as well. By the way, the concept of the progressive loading is that you show something to the user as soon as possible. So the first meaningful paint, largest contentful paint, and stuff like that. It can be even a placeholder. It can be even a loader. It doesn't need to be like an image. It can be a loader. And then over time you replace it with the actual image. I even heard one of the guys from the vue community mentioned to me another idea, which was about the sliders. So like image gallery, let's say. Because they usually use javascript for the gallery, the common libraries. So what he recommended is to, instead of loading the slider from the very beginning, because usually the slider is coming, for example, in the homepage. So having thing that relies on javascript on the homepage, which should be loaded as soon as possible, is not generally a good thing. So what he recommended is to display an array of images, just plain images. And then when the page loads, replace this array of images with the actual slider. So this can also be a trick, I would say. Yeah, cool. Totally makes sense. Cool. Let's now jump into our questions. So how about optimizing SVGs? To be honest, I'm not experienced in that. I'm sure there are packages to work with the SVGs in vue and Vid that does some of the optimization for us. I think it's SVGO, this plugin such as this one. But I don't have that much experience with svg, so not sure. We actually used before, I think you can just Google svg optimizer and then you just put your svg there, the one that designers give you, and it just removes all the blank spaces, optimizes everything. And sometimes you save up to like 80, 90% of space. I think it's even something like SVGOMG, something like this. I'm not sure if it's... I think it's by Jake Archibald. So something similar. Yep. Okay, cool. The second one is, does Cloudinary also work with vue? If not, are there plans to integrate it? Yes, so it works with vue, but with vue too. So for vue 3, I'm not aware of the library yet, but we plan with Colby, basically after developing the module for Next, to refactor it in the next version into a vue plugin, so that we will have the same experience for vue applications without actually having the Next module. So right now it's only in Next, but the idea is to extract it to vue and then use it in Next as a vue plugin. Okay, awesome. The next one is, any special considerations for optimizing images uploaded by a user? I would say the same ones. Like, try to... Maybe because users tend to put the best quality image because they are using it, so sometimes it's like 100% quality, and it is then used as a cover of something, or it's like cropped to smaller size. So maybe decrease the quality of the image. Like change the format for sure, because if the user uses PNG, as you've seen on demo, it's like 10 times bigger. So if it's possible, just try to optimize the format, the sizes as well, and yeah, the quality. I would name those three for sure. Makes sense. Of course, from the dev side, but as an end user, I always hate that when I just upload a high quality picture and they ruin everything. It's like in Messenger, right? You upload the image and there's like, the quality is so bad that you can't even see yourself in the image. But we all know the reasons for that. Next question, is Cloudinary free? Yes, yes, they have a free tier. The one that I was using during the demo is completely free. Of course, there are some limitations, as with all the tools, like with CMSs, with search engines, there are some limitations for the free tier. For example, in the case of the free tier in Cloudinary, what you do not get in a free account is the, for example, removing the background from the image. Because if you go and visit the website by Colby, he made it for the Next Cloudinary, the same approach as I showed you, but for next.js. He's using the remove background because he has the premium account as he's working there. And I have the free account, so basically I cannot do the removing background. When you try to send a request with the removing background parameter in the request, it will just say to you that your account is not suitable for removing background. So things like that. So in summary, yes, it is free. And I would say that the plan is quite generous. For smaller applications like portfolio, this is completely fine. For using it in a production application like enterprise, the free tier is not enough for sure. So you're an ambassador and they didn't give you a pro account. I created this account way before. I haven't requested yet the account for the ambassadors, but this is the plan. Yeah, you'll get it, you'll get it. Just joking. The next one is, is there an open source self-hosted alternative to Cloudinary? Yeah, so basically the IPX, the one I've shown. This one is open source. You can just download it. It's also used in the NextImage. So well, in NextImage you can also use Cloudinary as a provider, but by default it uses IPX so that you can use it locally without any third party service to optimize the image. Okay, thank you. Now we have a question that every developer likes. What happens when Cloudinary goes down? I would say that the same as with any other third party service. If our website is like, this third party service is crucial, the website will be down. That is the hard truth. But it can work the same way if we based our application on, for example, the CMS or the e-commerce. What happens right now if you have an e-commerce website that the front end is just the front end and the e-commerce platform will be down? Like, let's say that shopify for some reason is down. Basically, our shop is not working. So you can, of course, implement some kind of a fallback. Like if the request is failing, we can just do a fallback to a local directory where we might have some images or placeholder images and, yeah, the CMS as well. If your page is based on CMS, powered by CMS, if CMS doesn't work, your website doesn't work as well. Yep, 100%. And let's do a final question now. What else do you think is critical to optimize performance besides images? So besides images, some of the things that I mentioned in the lazy pattern, like fetching the data when it's needed, of course, the things that I mentioned in the very beginning of the talk, so optimizations on the back end. So if you can, for example, join some requests, let's say that the user is making three requests to get one data, maybe you can join those requests and then return the data to the user. Maybe if your users, your real clients, have slow internet connection, you can just use ssr applications and just render the data on the server and then return to the user the actual result so that he doesn't need to, you know, fire five requests. He can just fire one and get the data back. So there is actually a lot of things that we can do. And I created this checklist. So if you're interested, check it out. It's like front-end performance checklist. It's really easy to find. Yeah, this is a question you can probably talk about hours and hours. But yeah, thanks for helping us improving user experience. It was an amazing talk. Give us some noise for Jakub.
31 min
12 May, 2023

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

Workshops on related topic