10 Ways to improve your (Hugo) website performance

Website performance matters. Even if I would selfishly ignore all other human visitors to my website, I would be supporting Google’s mission to make the web faster and help myself climb the ranks of Google. In this article I describe 10 ways to make your Hugo website (or any other website) faster and give users a better experience.

These are the 10 tips I will be discussing:

  1. Conditionally load javascript files
  2. dns-prefetch and preconnect external page resources
  3. Move javascript files to the footer or using defer method
  4. Minimize third party requests
  5. Minimize your CSS stylesheets and javascript files
  6. Unify CSS and Javascript files
  7. Remove unused CSS
  8. Optimize your share (Icomoon) icons
  9. Resize and properly scale your images
  10. Serve images in next-gen formats

1. Conditionally load javascript files

Hugo pages are built based on multiple parts of a template. This means that the components are compiled into on page, for each individual page. The parts of the template can be found in the “partials” folder of your Hugo project.

Usually the page will compile based on standard logic; the parts/ components will be the same for each page, depending on the template you use. You can differentiate pages and their compilation based Page-level Params. These parameters can be defined for each specific page and can be used to differentiate any type of content that is generated on the page. For instance, the way javascript files or other elements in the head or footer section of the page are loaded.

conditionally load javascript

conditionally load javascript

2. dns-prefetch and preconnect external page resources

The dns-prefetch resource functionality is intended to look-up domain names before the browser would otherwise know that they’d be needed in the near future. It’s a quick way to speed up the loading of external page resources. You only need to set this up for resources that aren’t hosted on your own domain. On this website for example, I added the following lines to the

section, to ensure that the Google Tag Manager script (on domain www.googletagmanager.com) is available as soon as possible.

<link rel="dns-prefetch" href="https://www.googletagmanager.com">

The impact of preconnect and dns-prefetch tags has been tested by CSS Tricks in their article: “Using rel=preconnect to establish network connections early and increase performance”.

How to apply to Hugo sites

Externally hosted javascript files, images and css files can be part of any section of the page in Hugo templates. However, the usual suspects will be in the partials sections of the layout called:

  • head.html
  • footer.html
  • footer_custom.html

Open these partials and find any files that reference another domain. These are your potential files to improve.

Using dns-prefect and preconnect together

Both tags have very similar functionality, but I would still recommend using both. Browser compatibility is an issue, eventhough DNS-prefetching has been around since 2009 and preconnect since 2015.

Youi can expand my previous Google Tag Manager example with an additional line to include preconnect as well.

<link rel="dns-prefetch" href="https://www.googletagmanager.com">
<link href="https://www.googletagmanager.com" rel="preconnect" crossorigin>

A few simple DNS pre-fetch and preconnect rules

  • You only have to pre-fetch/ preconnect once per domain, per pageload. Which means that if you reference the same domain multiple times, for multiple scripts, you only have to pre-fetch / preconnect the domain once
  • Use both tags for browser compatibility

Resources about DNS pre-fetch and preconnect

3. Move javascript files to the footer or using defer method

When your pages loads, it’s starts at the beginning, which is the HEAD section. It moves through the code line by line, until it has parsed all the lines of code to load the page. Whenever something is loaded that takes a bit of extra time, like a big javascript file with lots of code in it, the loading of the next lines will wait until this is finished. As you can imagine, this can significantly slow down the loading time performance of the page as a whole. This also means the position of the script matters. You want the most important things on the page (the actual content) to load as soon as possible. All non-essential things should load after this. That’s why we add less essential javascript to the footer section instead of the HEAD section. You can do this with most javascript files, without it harming the functionalities on the page.

Disclaimer to using the defer method

You can probably imagine, the page will be rendered very quickly when using the defer method. By now, almost all browser are compatible with this method. However, always good to check the defer browser compatibility list to be sure your users will have a good experience.

4. Minimize third party requests

Third parties are other domains, that the browser connects to, to load all of the content and functionalities on the page. This can be anything from a tracking script (ie Google Analytics or Facebook tracking pixel) or a javascript library used to run specific functionalities (like jQuery).

Every time a browser has to connect with another domain, the browser needs to make a DNS lookup, SSL handshake, connection etc. This can easily take up to 300 milliseconds per domain. Precious time for a user that will hurt his experience. On top of this, we don’t have any control over the cache of the these files. This is handled by the domain itself. In some cases, this may mean that some of these files have to go through the same process every time they are requested. This slows the website down every time the file is loaded…

How to speed up third party requests

Some of these recommendations have been mentioned before, but are applicable to third party files as well:

  • Use the defer method
  • Use preconnect
  • Conditionally load the files

There are some additional ways to deal with third party files too.

Host the files locally

In addition, a great way to get more control of the third party requests is by hosting them locally. From a performance perspective, it’s almost always best to reduce the number of third party requests. We have some recommendations before you start hosting files locally:

  • Check your server speed with tool like Pingdom. Hosting locally only works best if you can match the external location the file is hosted
  • Big files can take up a lot of bandwidth from your server. Costs may be quite high to host them locally
  • Check where the file is hosted. If it is a CDN like Google, it’s going to be difficult to match their speed, hosting locally might not do much for a better website performance
  • Check if the file is updated frequently. Javascript libraries may be updated without you knowing it. If you decide to locally host, you may end up with an outdated version of the file.
Pingdom overview

Pingdom overview

5. Minimize your CSS stylesheets and javascript files

Size matters, especially for website performance. That’s why it’s also a good idea to minify (large) javascript and css files. When you minimize a javascript or css file, you are basically removing all unnecessary characters, without impacting the functionality it is used for. It will remove whitespace, comments and other unused characters. It will also shorten javascript function and variable names. This means you are going to get the same functionality, but less Kb’s sent to the browser.

Keep in mind that a minimizing a small css file or javascript file will only provide you with marginal gains. They may not be worth minimizing at all. The bigger the file, the bigger the impact.

How to minimize javascript and css files

Hugo has built in features which will minify your css and javascript files. Simply provide the assets or files you would like to minify and add the function resources. Minify to your template to automatically let Hugo minify your files for you. Check out the Hugo website for information about Asset minification.

If you are not running a Hugo website, you can use the CMS functionalities that may be provided by your CMS or use an online javascript/ css minification solution like Javascript-minifier.

6. Unify CSS and Javascript files

For every file that is referenced in your code, the browser has to make a request. Every request takes time to process for a browser. This means that every file costs time to download. That’s why I recommend to unify css and javascript files. This way the number of requests by the browser is reduced and your website will load faster.

How to unify CSS and Javascript files

To unify your files and minimize the number of requests, Hugo has another cool built-in feature. Asset Bundling allows you to automatically assign CSS files or javascript files and bundle them into a new file, which will be referenced on the page, instead of the separate files.

If you are not running or Hugo website, or you want more control over the process, you can look for the CSS or Javascript folder and combine the files manually. Make sure to only unify css and javascript files if files are not conditionally loaded, otherwise you will include javascript or CSS code on pages where it is not used.

7. Remove unused CSS

Most websites are not developed starting with a blank page, but instead are started by using a template that already exists. After editing, removing or ignoring features and making the template work for your website, you may end up with some code that is not longer used. The CSS files may end up being bigger than they have to be. Cleaning up CSS after the first part of the development process may be very beneficial. It’s like cleaning your closet, it’s just good practice to do every now and then!

How to remove unused CSS

It can be pretty challenging to do this, because the CSS code you use on the homepage may be different from the CSS code you use on an article page and it will vary for other pages as well. This is also where a lot of online tools go wrong. They will only check one or a few pages on your website and return a smaller CSS file. This may leave you with missing CSS on some pages. Hardly any solution will crawl your entire website to provide you with the correct, used CSS references that are being used. So unless you have a one-page website, automated solutions will probably not be helpful.

Hugo provides us with several solutions that can help us process our CSS files and return a correct CSS file. SASS / SCSS or PostCSS files can be automatically processed by Hugo, depending on your site configurations. Unfortunately, my website is not being using these functionalities, just like most R Blogdown websites.

Using Node.js solutions to remove unused CSS

For those with some technical skills, you may want to have a look at some of the following solutions, which allow you to run an application to check all CSS references on your pages and check if they are defined in your CSS files. Any unused CSS references in the CSS files will be removed, leaving you with a clean version of your CSS.

Some solutions to consider:

Non-technical way to remove unused CSS

There is an alternative for all of those non-technical website owners. This solution will only work on small sites with relatively few pages. Here are the steps to get to a clean css:

1 Find your XML sitemap or create one. This is my sitemap 2 Extract the URL’s of your sitemap 3 Open Purify CSS Online in a second tab 4 Copy/paste each url from your sitemap to the Purify CSS field 5 Run Purify CSS and download the clean, new CSS files

Obviously, this will only work on small websites, with few URL’s to copy/paste and is at your own risk. If you’re still early in the development of your website, it will give you a clean CSS to start your endeavors with.

8. Optimize your share (Icomoon) icons

Particularly on blogs, we love to make our content shareable. This will often create more reach for our content on social media platforms and is very helpful to generally expand your audience.

Unfortunately some of the biggest social sharing providers are absolutely horrible from a performance and privacy/security perspective.

I have decided to use social share buttons based on Icomoon icons. Icomoon has icons for all possible occasions, including icons for social media logos. It’s a pretty lightweight solution, but with a few issues I wanted to solve before implementing it on my website:

  • Out-of-the-box, it requires you to reference files from their server, which is just another third party I don’t want to reference
  • You will get a lot of waste, unused css and larger files than you really need.

How to implement Icomoon share icons

The issues forced me to do a few things:

  • pull all the font-awesome files locally
  • remove all the unused CSS.
  • Instead of adding another CSS file to every page, I included the required CSS to my existing CSS file instead
  • Reference the SVG version of the icons sprite to choose my prefered colors

This is the code of my partial layout for the social sharing buttons

{{ if or .Params.socialShare (and .Site.Params.socialShare (ne .Params.socialShare false)) }}
<!-- Social Share Button HTML -->
<div class="share-box">
    <ul class="share">
      <!-- Twitter -->
      <li>
        <a class="fs2" href="//twitter.com/share?url={{ .Permalink }}&amp;text={{ .Title }}&amp;via={{ .Site.Author.twitter }}" target="_blank" title="Share on Twitter" rel="noreferrer" aria-label="share article on twitter">
          <i class="icon-twitter"></i>
        </a>
      </li>
  
      <!-- Facebook -->
      <li>
        <a class="fs2" href="//www.facebook.com/sharer/sharer.php?u={{ .Permalink }}" target="_blank" title="Share on Facebook" rel="noreferrer" aria-label="share article on facebook">
          <i class="icon-facebook"></i>
        </a>
      </li>
  
      <!-- Reddit -->
      <li>
        <a class="fs2" href="//reddit.com/submit?url={{ .Permalink }}&amp;title={{ .Title }}" target="_blank" title="Share on Reddit" rel="noreferrer" aria-label="share article on reddit">
          <i class="icon-reddit"></i>
        </a>
      </li>
  
      <!-- LinkedIn -->
      <li>
        <a class="fs2" href="//www.linkedin.com/shareArticle?url={{ .Permalink }}&amp;title={{ .Title }}" target="_blank" title="Share on LinkedIn" rel="noreferrer" aria-label="share article on linkedin">
          <i class="icon-linkedin"></i>
        </a>
      </li>
      </li>
    </ul>
  </div>
  {{ end }}

By including some basic CSS and ionmoon font files, I optimized the performance of the share buttons and run them locally.

This method provides me with very fast, elegant social share icons that I can easily configure. Check them out at the bottom of this page :-)

9. Resize and properly scale your images

We’ve all been there. You add an image to a webpage and it is simply too big. We are putting a very large image into a very small space. This means that you are adding a lot of extra kilobytes to a a website for no good reason. Reducing the image to the correct size for a space on the page can make a huge difference to the performance of the page.

How to properly resize an image

It may seem very simple to resize an image to the correct size, but looks can be deceiving.

To make sure you know the minimum size of an image you need to check a few things:

  • Is the image size set absolute? When an image has an absolute size (ie px, cm, mm, inch), this is fairly straightforward. You can rescale your image to the correct number of pixels
  • Is the image size set relative? When an image is scaled in percentage, you have to consider that the page may rescale as well, which means the image has to be appropriately sized based on the maximum width of the page. For more details on how to keep this kind of responsive scaling in mind, check out this article by CSS-Tricks.

Tools to determine the correct and current image size.

There are some useful tools available to determine the current image size and the dimensions of the location on the page it is put in. A Chrome extension I often use if Image Size Info. Just select the image you want to optimize and it will show you its current size and proper dimensions.

Image Size Helper Chrome

Image Size Helper Chrome

Alternatively, you can use the Chrome Developers tool. Follow these steps to find out the image sizes:

1 Use the Chrome browser and navigate to the page with the image 2 Click the right mouse button on the image you want to investigate 3 Select “inspect” to open the developer console 4 Now hover the image URL that is highlighted to see the image dimensions. The intrinsic values will show you the actual size of the image, the regular values show you the size of the element on the page.

The bigger the differences between the actual size of an image and the dimensions of the image on the page, the larger the performance gain you can make when you resize it.

Tools to resize an image

Once you determine which image to resize, you can use several tools to resize it to the appropriate size. Here are some examples:

10. Serve images in next-gen formats

Images can make or break website performance. They use up a lot of bytes to be processed by the browser when they are loaded. That’s why companies like Google have developed new images to next-gen formats that allow you to optimize the performance of your website. These next-gen image formats are about 25% lighter, faster to load and very flexible. Popular ones are JPEG 2000, JPEG XR, and WebP. Browsers are embracing these types of images to make the web faster.

Browser compatibility

Unfortunately, not all next-gen image format are supported by all browsers yet. Here’s an overview of compatibility per next-gen format:

This makes it a bit tricky to implement. If you feel it is still worth it to upgrade your images, I have 2 possible ways to do this.

Choose the image type that’s most compatible

The easiest way to use next-gen images is by picking the one with the highest browser compatibility and simply changing your images to the new image type and you’re done! Make sure to determine what the right image type is, based on the current compatibility for each image type and the number of users that use these browsers. You can check this in Google Analytics in your browser reports. Keep in mind that whenever an image is not compatible with a browser, it won’t show. So only change images if you are willing to accept this risk.

Manage multiple types of images and update your code

As an alternative, you can use multiple types of images, to populate on spot. Which means you create multiple versions of the same image, which creates a fallback system. It’s a bit more work, but will allow you to provide the fastest experience possible for your users. To reference multiple image types in your HTML code, you need to update how you add the image. Here’s an example:

<img src="image.jpg" alt="my image" />

By using the picture reference, the browser will check compatibility for the webp version of the image first and otherwise reference the “old” jpg version

<picture>
  <source srcset="image.webp" type="image/webp">
<img src="image.jpg" alt="my image">
</picture>

At the time of writing there is no way to process the conversion of images in the Hugo framework. A milestone has been marked, but not yet completed. I wrote a simple shortcode that allows you to include next-gen images in your Hugo website.

Tools to convert to next-gen image formats

There are several (online) tools available that can convert your png/jpg images to webp or other next-gen formats. Here are a few of my favorites: