Hugo 0.43 Asset Pipeline: Minify HTML and concat JS files


Just played around with Hugo 0.43 - amazing. Here are examples that work for me:

I do HTML minification with html-minifier and npm which I included in my production script.

1.: npm install html-minifier -D which saves it in the dev dependencies of your package.json
2.: Build your site
3.: html-minifier --input-dir build/public --output-dir build/public --file-ext html --collapse-whitespace
3a.: html-minifier --input-dir build/public --output-dir build/public --file-ext xml --collapse-whitespace

My production script is named and includes the following:

#!/usr/bin/env bash

HUGO_ENV="production" hugo -d build/public --config config.toml,config_public.toml

html-minifier --input-dir build/public --output-dir build/public --file-ext html --collapse-whitespace

The CSS is handled in layouts/_default/baseof.html like so:

{{ if ne (getenv "HUGO_ENV") "production" }}
    {{ $styles := resources.Get "scss/main.scss" | toCSS (dict "enableSourceMap" true) }}
    <link rel="stylesheet" href="{{ $styles.Permalink }}" media="screen">
{{ else }}
    {{ $styles := resources.Get "scss/main.scss" | toCSS | postCSS (dict "use" "autoprefixer") | minify | fingerprint }}
    <link rel="stylesheet" href="{{ $styles.Permalink }}" integrity="{{ $styles.Data.Integrity }}" media="screen">
{{ end }}

To use PostCSS you must install PostCSS CLI with npm i -D postcss-cli and it will be stored in your package.json in dev dependencies.

Here’s also my Javascript asset handling (in this project I’m working with Foundation for Sites) which works like a charm with the new hugo version. For the time being I have an automated npm script which copies the JS files from node_modules into ./assets/js and the layouts/_default/baseof.html calls it from there.


#!/usr/bin/env bash

cp -rf ./node_modules/foundation-sites ./assets/js'
cp -rf ./node_modules/jquery ./assets/js'
cp -rf ./node_modules/motion-ui ./assets/js'
cp -rf ./node_modules/what-input ./assets/js'

This is the JS part from layouts/_default/baseof.html:

{{/*<!-- NOTE: Vendor Scripts -->*/}}
{{ $jquery := resources.Get "/js/jquery/dist/jquery.js" }}
{{ $foundation := resources.Get "/js/foundation-sites/dist/js/foundation.js" }}
{{ $whatinput := resources.Get "/js/what-input/src/what-input.js" }}
{{ $motion := resources.Get "/js/motion-ui/dist/motion-ui.js" }}

{{/*<!-- NOTE: User Scripts -->*/}}
{{ $user := resources.Get "/js/user.js" }}
{{ $scripts := resources.Get "/js/scripts.js" }}

{{ if ne (getenv "HUGO_ENV") "production" }}
    {{ $vendorscript := slice $jquery $foundation $whatinput $motion | resources.Concat "/js/vendor.js" }}
    <script src="{{ $vendorscript.Permalink }}"></script>

    {{ $userscript := slice $user $scripts | resources.Concat "/js/user.js" }}
    <script src="{{ $userscript.Permalink }}"></script>
{{ else }}
    {{ $vendorscript := slice $jquery $foundation $whatinput $motion | resources.Concat "/js/vendor.js" | minify | fingerprint }}
    <script src="{{ $vendorscript.Permalink }}"></script>
    {{ $userscript := slice $user $scripts | resources.Concat "/js/user.js" | minify | fingerprint }}
    <script src="{{ $userscript.Permalink }}"></script>
{{ end }}

The workflow generates two JS files: vendor.js and user.js. This is because the vendor JS files normally don’t change but the custom JS files do change quite often. In a later step I’ll implement, that only the custom JS files will be re-generated on change.

About the minifier used in Hugo Pipes in a totally unrelated context

Thanks for this solid info! I’m curious who are you using for your deploys / hosting?


I’m in Germany and since 1 year I use a professional hoster named All-Inkl. I used to work 11 years with Strato but they focus nowadays more on the consumer so I couldn’t configure the webspace accordingly to my needs. My new hoster is not only cheaper than Strato but also very responsive if I have support questions.

I deploy with rsync via the SSH access.

Hope this helps.


@Leo_Merkel - Thanks man! As noob still trying to get my head around hugo, your explanation really helped me. I’d be interested in how you later set up regeneration of only the custom JS only - if you feel inclined to share.
Thanks again.