Go Back

How to cache-bust and concatenate JS and SASS files with Hugo in 2018

Author: Ben Bozzay

Follow me on Twitter for more Hugo, front-end development, and digital marketing tutorials.

Hugo released version 0.43 adding built-in asset post-processing. As our team wraps up development of a free page-builder theme that utilizes Forestry.io, Netlify, and Hugo, we were thrilled to replace our asset processing tasks with Hugo’s pipeline.

The addition of built-in asset pipeline support allowed us to simplify our build process and reduce potential conflicts caused when deploying with Netlify. Here’s how we replaced our Gulp tasks with Hugo’s new asset pipeline to add SASS support and to concatenate, minify, and fingerprint JS and CSS.

Built-in SASS support

Since Hugo previously did not provide built-in support for SCSS, Gulp was commonly used for SCSS generation. This wasn’t ideal for us because the build and deploy process with Netlify would sometimes encounter issues. As a result, we would just commit processed assets instead of using Netlify to build assets.

Using SCSS with Hugo in 2018

Sass provides many benefits that simplify theme development. For example, it enables the use of variables for CSS values.

$font-stack:    Helvetica**,** sans-serif;  
$primary-color: #333;  
body {  
  **font**: 100% $font-stack;  
  **color**: $primary-color;  

This is especially useful for theme development when user’s are able to define these variables themselves and quickly change the style of a theme.

Partial Sass files allow you to better organize your CSS. These files, which use a leading underscore, are then imported and combined into a single file.

Note: you must use the extended version of 0.43 for Sass support.

  1. In your theme folder or the top-level of your project add an assets folder to store your SCSS:

By default, resource objects live in the /assets/ directory (you will most likely need to add this folder).

  1. Add a main.scss file to your scss directory:

  1. In main.scss, import your partial SCSS:

If you define variables that are used in other partial files, you must import that partial file first. For example, we use a file called _variables.scss to define fonts and colors.

  1. Generate the resource:
{{ $styles := resources.Get “scss/main.scss” | toCSS }}

You can learn more about the resource functions within #4854. This seems to provide more context then the release page.

For example, resources.Get creates a new resource object based on a path to a file within the /assets directory.

In the code snippet above, resources.Get generates a file in the resources directory:

In this particular example fingerprinting is enabled, which is why this file has a hash

  1. Reference the newly generated SCSS file:
{{ $styles := resources.Get “scss/main.scss” | toCSS }}<link rel=”stylesheet” href=”{{ $styles.Permalink }}” media=”screen”>

Appending .Permlink to the styles variable generates the URL needed to reference the scss file in our resources directory.

Cache-busting using fingerprinting

Cache busting allows you to force the browser to re-download files that are usually cached. CDN’s like Cloudflare and a user’s browser will cache certain resources like stylesheets to reduce page load time. This means that a user might not see newly committed stylesheet updates unless the browser cache is cleared.

If you are using Cloudflare, you’d need to enable development mode and clear the Cloudflare cache before committing your stylesheet update so that user’s browsers do not cache Cloudflares cached version of your stylesheet :).

Fingerprinting generates a unique hash when you recompile your stylesheet. This means that the file name changes every time you commit your stylesheet. Cloudflare and the user’s browser will re-download the asset since the name changed.

We can also minify our stylesheet to reduce the file size. Resources functions like toCSS, minify, and fingerprint can be chained!

  1. Add fingerprinting and minification to your stylesheet:
{{ $styles := resources.Get “scss/main.scss” | toCSS | minify | fingerprint }}
  1. Add .Data.Integrity to fingerprinted resources.
<link rel=”stylesheet” href=”{{ $styles.Permalink }}” integrity="{{ $styles.Data.Integrity" media=”screen”>

According to this issue, Data.Integrity should be added to fingerprinted resources to provide verification that changed files are not malicious.

Overwriting your theme’s SCSS

If you have an assets folder in the top-level of your project and an assets folder in your theme directory, Hugo will prioritize the top-level directory over the theme directory. This means that you can overwrite theme files without modifying the theme directory. This is similar to how Wordpress child themes work.

  └── scss  
  |   └── _accordian.scss  
  └── pancakes  
  |   └── assets  
          └── scss  
              └── _accordian.scss

In our particular scenario, we plan to release a free, open source builder theme as a git submodule. This will allow us to push theme updates. However, we can provide user’s with guidance on how to safely overwrite theme layouts and scss files without conflicting with theme updates.

Combining JS files

Hugo 0.43 added resources.Concat, which means that we can combine JS files without using a bundler.

Directory priority is still the same:

  └── js  
  └── pancakes  
  |   └── assets  
          └── js

JS within the top-level assets folder will overwrite identically named JS within the theme assets folder.

  1. Create resources out of your JS files in your assets folder:
{{ $vendor := resources.Get "js/vendor/jquery.min.js" }}  
{{ $customjs := resources.Get "js/main.js" }}
  1. Create an array using slice:
{{ $scripts := slice $vendor $customjs | resources.Concat "app.js" | minify | fingerprint }}

In this example, resources.Concat generates the filename “app.js” in the resources directory. Files are combined based on the argument order.

Chain minify and fingerprint to reduce page load time and force a cache refresh.

  1. Link to the generated resource
<script type="text/javascript" src="{{ $scripts.Permalink }}" integrity="{{ $scripts.Data.Integrity }}" media="screen"></script>

Make sure you use integrity to verify the authenticity of files that use a hash (like fingerprinted files).

Hugo’s bright future

This new version of Hugo added a few other fantastic features, such as image processing and the ability to use Go within resources.

Resize, crop, and re-sample programmatically.

A user uploaded image via a CMS like Forestry.io could now result in several generated resources, such as a cropped or scaled thumbnail.

For example, a cropped thumbnail for a blog archive page:

Hugo uses Smartcrop by default

A full-size version of the image for the actual blog post:

Cropped thumbnails are essential for a blog and are a standard for content management systems like Wordpress. A blog archive page needs to use cropped or scaled down versions of the post’s featured image to avoid horrendous page load speed from downloading several full-size images.

One work around for this problem is to upload two variations of the featured image, but now we can simply include image processing within the layout!

Using Go within resources

This requires further exploring, but this likely means that you could use page or site-level variables to manipulate stylesheets. Users could potentially use front matter to define Sass variables (though you have to be careful with this approach). This is particularly game-changing if you overlay a CMS like Forestry.io and use Data files to provide Wordpress-like theme options. Users could have direct access to modifying the look and feel of their theme without writing any code.

Bridging the gap

Static site generators are increasingly becoming more accessible to non-developers. The latest update from Hugo will enable normal users to benefit from the speed and functionality of Hugo without writing a line of code themselves.

  • Netlify’s one-click deploy enables simple deployment of Hugo with a pre-installed theme. It also enables contact forms for static sites and simple one-click SSL install.
  • Forestry.io gives users a familar and easy to use interface while allowing developers to provide simple fields for modifying layouts and styles.
  • Hugo’s fast build time, markdown shortcodes, newly added asset pipeline, image processing, and ability to modify stylesheets using front matter, make it a worthy static competitor to Wordpress.

What’s missing?

This combination allows us to produce a one-click deploy theme that integrates with Forestry.io to provide users with a flexible page builder similar to Wordpress page builders like Divi, Visual Composer, and Beaver Builder.

In the coming weeks we will release a free, open source Hugo theme that enables users to easily build page layouts without writing a line of code or using Git.

Follow me on Twitter: https://twitter.com/BenBozzay