Hugo - Asset Bundling, Minification, and SRI Fingerprinting

As a general rule, if you make fewer, smaller requests, your website is going to load quicker. However many Hugo themes include multiple JS and CSS files, and don’t (by default) either minify them or concatenate them. Here’s how.

HOWTO

For this example, I’m going to bundle up the JS files in my site, but this approach works equally well for CSS files.

Firstly, create a file named _scripts.json somewhere in your source tree. (I chose to start the name with an underscore so it came at the top of the list of files – you can name yours whatever you want).

This file should contain a list of all the scripts you wish to bundle:

{
  "scripts": [
    "js/foo.min.js",
    "js/bar.min.js",
    "js/custom.js",
    "js/etc.js"
  ]
}

Next, find the place where your scripts are normally included in the page (for JS: normally the footer; for CSS: normally the header).

{{ $assetBusting := not .Site.Params.disableAssetsBusting }}
{{ $scripts := getJSON "path/to/_scripts.json" }}

{{ $.Scratch.Set "jslibs" slice }}
{{ range $scripts.scripts }}
{{ $.Scratch.Add "jslibs" (resources.Get . ) }}
{{ end }}

{{ $js := .Scratch.Get "jslibs" | resources.Concat "js/combined-scripts.js" | resources.Minify }}

<script src="{{ $js.RelPermalink }}{{ if $assetBusting }}?{{ now.Unix }}{{ end }}"></script>

With that in place, you can delete all the other <script> entries, provided the relevant script is included in your list.

(Design choice: why not simply iterate over a directory of scripts? The downside of that approach is that you don’t get to choose the order of the scripts, unless you name them something like 01-foo.js, 02-bar.js. I thought it was less surprising to have an explicit list).

Bonus: SRI Fingerprinting

Since we’re going to the effort of setting up a pipeline for asset processing, we might as well include SRI (subresource integrity) fingerprinting.

Hugo already has support for this, and the change is simple:

Where you create the final resource, simply fingerprint it:

{{ $js := .Scratch.Get "jslibs" | resources.Concat "js/combined-scripts.js" | resources.Minify | fingerprint }}

And where you consume that resource, apply the integrity value:

<script
    src="{{ $js.RelPermalink }}{{ if $assetBusting }}?{{ now.Unix }}{{ end }}"
    integrity="{{ $js.Data.Integrity }}"
></script>
Pinterest
Reddit
Hacker News

You may be interested in...

Hackification.io is a participant in the Amazon Services LLC Associates Program, an affiliate advertising program designed to provide a means for sites to earn advertising fees by advertising and linking to amazon.com. I may earn a small commission for my endorsement, recommendation, testimonial, and/or link to any products or services from this website.

Comments? Questions?