Ways to improve website performance in NUXT JS

Ways to improve website performance in NUXT JS

ยท

8 min read

Being a front-end web developer, I am always curious about different ways we can make our websites load faster delivering the best user experience. In terms of performance, we always consider SSR (Server Side Rendering) over CSR (Client Side Rendering). But choosing an SSR framework doesn't necessarily mean performance will be improved automatically. In this article, we will discuss various tools out there to test where our website stands in terms of performance and look for proven ways to increase it. We'll be using an SSR Vue framework called NUXT JS as a reference. So, let's get started.

Parameters for measuring performance

Here are some metrics in which performance is measured:

  • First Contentful Paint (FCP): It is the time taken by a webpage to show the first bit of content from DOM to the user, for example, images, loading spinners, and placeholders of images.

  • Time To Interactive (TTI): A metric that measures the time taken for the website to be user interactive.

  • Total Blocking Time (TBT): It is the total amount of time your website is blocked for user interactivity. TBT is considered a dependent factor for TTI. If the main thread is blocked due to some activity, TTI will increase as well.

  • Speed Index: Speed index lets us know how fast the visible content is loaded i.e. above-the-fold content (viewport) of the website. The lower your speed index, the faster your website is actually performing.

    More about Speed index can be checked here.

  • Largest Contentful Paint (LCP): Largest contentful paint measures the time at which the largest element above the fold is rendered.

  • Cumulative Layout Shift: It is the time taken for the webpage to be visually stable. For instance, a webpage loading a UI component with API data may cause page jumps which is unappealing to the user. This webpage will take more time to be in stable view contrary to a webpage with defined UI placeholders/skeleton loaders until that async component is loaded.

Tools for testing website performance

Lighthouse

Lighthouse chrome extension is a great tool to test a webpage's performance, SEO score, Accessibility, and other best practices. Apart from giving the respective results, it also gives a full report with suggestions. Just search Light House in the google search tab and download the extension.

Screenshot 2022-03-04 at 6.34.39 PM.png

Google Page Insights

Google introduced its own tool to test webpage performance. Same as the Light House, this tool is specifically focused on giving performance-related scores and suggestions to improve on. Here is the link: pagespeed.web.dev.

Screenshot 2022-03-04 at 6.38.06 PM.png

Ways to increase performance in NUXT JS

Optimize images

Images constitute a large part of web page's size. Having the correct dimension of images according to screen size reduces the Total Blocking Time of the website. This in turn increases the web page's performance manifolds!

Below are some proven tips to optimize images in NUXT JS

Use loading=lazy attribute for img tags

Using this attribute, images are loaded lazily when they reach the viewport. So, the web page will have less data to load at first.

<img src="src" alt="Mountains" style="width:100%" loading="lazy" />

Note: Do not use this property for above-the-fold images i.e. images that are already in the viewport. This might increase the Largest Contentful Paint time along with increased speed index score. This affects the performance negatively.

Giving explicit height and width to image assets.

This warning will come after running lighthouse if we haven't specified height and widths for the images. Normally, for making an image responsive, we use this CSS property.

img {
   max-width: 100%;
   height: auto
}

This makes images responsive and takes the parent's height and width. But if we lazy load images, the content below the images will start jumping as there is no height and width assigned. The better approach is:

img {
   width: calc(100vw - (some calculation according to your image))
   height: calc(100px + (some calculation according to your image))
   max-width: 450px;
   max-height: 550px
}

Considering the above styles, we're letting the browser know that width and height of images are these values. So, even if image is lazy loaded, it will have the correct placeholder height and width. Due to this, the content of the website will not jump and web page's Cumulative Layout Shift will decrease, increasing the performance in general.

Use services like Cloudinary to store images.

Cloudinary is a great tool that lets us optimize images with on-the-fly transformations. Few advantages of using Cloudinary service:

  • It provides a CDN service so that images and videos load faster.
  • Serving images that best fits a particular browser, for example: webp format offers a better compression hence reducing the image size.
  • Get image of any height/width/aspect ratio optimized from their end only. Since large network payloads will be reduced and we get optimized images according to device size, the website will load faster.

Let's create a free account on cloudinary and copy the sample image url in Cloudinary. Now let's use it in our NUXT app as:

Screenshot 2022-03-06 at 6.25.34 PM.png

<template>
 <img src="https://res.cloudinary.com/test-1/image/upload/f_auto/sample.jpg" />
</template>

For auto fetching format, we use f_auto in the image URL. We can update the url for fetching image according to our needs. For fetching image of particular height and width, let's we can update the url as follows:

<img src="https://res.cloudinary.com/test-1/image/upload/f_auto,w_0.5,h_100/sample.jpg" />

This url will fetch width 50% of its original dimension and height of 100px.

Now, run the local server and open the network tab. We can see although it was a jpg image, it was changed to avif format which supports the best compression for our browser. How cool is that! ๐Ÿ˜Ž

Screenshot 2022-03-06 at 5.59.09 PM.png

Now, let's select a device and check the image size.

Screenshot 2022-03-06 at 6.08.35 PM.png

We saw that it's coming around 41.7 KB which is quite large for a mobile device. Now, let's update the img URL for mobile devices and make a guess, reducing the width to 70%. The url will look like: https://res.cloudinary.com/test-1/image/upload/f_auto,w_0.7/sample.jpg and check the size now.

Screenshot 2022-03-06 at 6.13.02 PM.png

The image size was reduced to 27 KB! ๐Ÿ˜ฒ

Now a question comes, how will we generate a url for different devices. We have <picture> tag to our rescue ๐Ÿ˜„. Picture tag provide a way to define srcset for different screen sizes, so that we don't have to do it manually.

<picture>
  <source media="(max-width:650px)" srcset="https://res.cloudinary.com/test-1/image/upload/f_auto,w_0.7/sample.jpg">
  <img src="https://res.cloudinary.com/test-1/image/upload/f_auto/sample.jpg">
</picture>

Using preload attribute in video tag.

This option provides a hint to the browser how video data should be loaded on page load. There are 3 options available for this attribute: auto, metadata and none.

  • auto: The browser loads the entire video when the page loads.
  • metadata: The browser loads only metadata when the page loads.
  • none: The video is only loaded when the play button of video controls is clicked.

We can use any of the above options which best fit for our users experience.

<video controls preload="none">
  <source src="src" type="video/mp4">
</video>

Note: The preload attribute is ignored when the video is set to autoplay.

Nuxt JS prefetches the Javascript for <nuxt-link> links so that subsequent request to another route is faster. Sometimes, we don't need this behaviour as our page is getting slowed slow due to some third-party libraries. The solution is to use prefetch attribute here.

<nuxt-link to="/about" :prefetch="false" />

Async load external scripts

When using any third-party JS script in nuxt.config.js, add async: true or defer: true so that it doesn't affect the main UI thread. This helps decreasing the Total Blocking Time metric time.

Use computed property instead of reactive data property for static data.

For using any static image in the Vue template, we place it inside data property. Since the static data is not going to change, using a computed property (which uses caching) here is a lot better approach.

Using Data:

import SomeImage from '~/static/images/image'

export default {
  data() {
    return {
      SomeImage,
    }
  },
}

Using Computed:

import SomeImage from '~/static/images/image'

export default {
  computed:{
    someImage() {
      return SomeImage
    },
  }
}

Remove components: true from nuxt.config.js

export default {
  head: {
    title: 'nuxt-app-example',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
    ],
    link: [
      { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
    ]
  },
  components: true, 
  buildModules: [
  ],
}

Although, using this option is a great way to auto-import all the components in our pages, but as the project grows, this option leads to a lot of issues. Suppose we want to use a specific set of components in functionality, and they are not being used anywhere else. This may lead to enormous amount of build time and memory consumed. A better approach here is we can use Vue.component to make the actual common components global. Also, one more approach here is to assign common components array to components.

export default {
   components: ['component_1', 'component_2'], 
}

Avoid using 3rd party libraries if native options are available

Now, this statement may not apply to most be our projects as they are time-based, we need to complete them ASAP so third-party libraries are the best option here. But they come with a downside as well. We cannot manage or edit the code, we are not sure if best practices are being followed. Also, to use one functionality, we end up adding a lot of unused code to our projects. This in turn affects the performance of the web page.

Conclusion

I hope we got some great ways to enhance our website performance. Do experiment and see the results yourself. ๐Ÿ˜Š

Thank you for reading!