Master npm with Hugo: customizing build scripts
In a series of three posts I will introduce you to npm, show you how to manage dependencies, and show you how to customize build scripts. This is the third post of the series.

In the second post of the series, I showed you that Hugo itself can be seen as a dependency of your project, and that a starter theme provides you with full control over your dependencies and gives you the possibility to customize build scripts.
We also extended our basic example with a local Hugo install, a Hugo project structure, and a starter theme structure.
Customizing scripts
Now weβre ready to talk about how to customize build scripts. In stead of using npx
to run a script from a local npm package, we’re going to add a number of custom
scripts to our package.json
:
{
..
"scripts": {
"prestart": "npm run clean",
"start": "hugo server --disableFastRender",
"prebuild": "npm run clean",
"build": "hugo --minify",
"clean": "shx rm -rf public resources",
"test": "echo \"Error: no test specified\" && exit 1"
},
..
}
Note the pre scripts prestart
and prebuild
. The prestart
script e.g. executes before the start
script. Similarly, you can use post scripts. A poststart
script e.g. would execute after the start
script.
Listing scripts
The npm run
command lists the scripts available in your package.json
:
$ npm run
Lifecycle scripts included in npm-hugo:
prestart
npm run clean
start
hugo server --disableFastRender
test
echo "Error: no test specified" && exit 1
available via `npm run-script`:
prebuild
npm run clean
build
hugo --gc --minify
clean
shx rm -rf public resources
Running scripts
The npm run start
command e.g. will:
- delete the temporary build directories, and next
- start the hugo server with the
--disableFastRender
flag enabled.
Bootstrap
We can use dependencies to pull in resources for our project. Let’s import the Bootstrap SCSS we added previously, into our starter theme:
shx mkdir assets && shx echo '@import \"bootstrap/scss/bootstrap\";' > ./assets/app.scss
And process the SCSS with Hugo in ./layouts/partials/head.html
:
<head>
{{ $options := (dict "targetPath" "main.css" "includePaths" (slice "node_modules")) -}}
{{ $css := resources.Get "app.scss" | resources.ToCSS $options -}}
<link rel="stylesheet" href="{{ $css.Permalink }}">
</head>
Let’s add some Bootstrap styling to ./layouts/index.html
:
{{ define "main" }}
<div class="container-fluid">
<div class="row vh-100 align-items-center justify-content-center">
<div class="col-md-7">
<h1 class="text-center">{{ .Title }} π</h1>
</div>
</div>
</div>
{{ end }}
Add the resources
and public
folders to .gitignore
, and check the result with npm run start
.
Removing unused CSS
We can also use dependencies to pull in tooling for our build process. Let’s make our project production ready by stripping out unnecessary CSS:
PurgeCSS analyzes your content and your CSS files. Then it matches the selectors used in your files with the one in your content files. It removes unused selectors from your CSS, resulting in smaller CSS files
We will also be installing Autoprefixer, a PostCSS plugin to parse CSS and add vendor prefixes to CSS rules using values from Can I Use:
npm i -D @fullhuman/postcss-purgecss postcss-cli autoprefixer
Add ./postcss.config.js
:
const autoprefixer = require('autoprefixer')
const purgecss = require('@fullhuman/postcss-purgecss')
module.exports = {
plugins: [
autoprefixer(),
purgecss({
content: ['./layouts/**/*.html']
})
]
}
Update ./layouts/partials/head.html
:
<head>
{{ $options := (dict "targetPath" "main.css" "includePaths" (slice "node_modules")) -}}
{{ $css := resources.Get "app.scss" | resources.ToCSS $options -}}
{{ if hugo.IsProduction -}}
{{ $css = $css | minify | fingerprint | resources.PostCSS | resources.PostProcess -}}
{{ end -}}
<link rel="stylesheet" href="{{ $css.Permalink }}">
</head>
Update ./config.toml
:
baseURL = "/"
..
Add to package.json
:
{
..
"browserslist": [
"defaults"
],
..
}
Run npm run build
and check the result. That’s it. We’ve brought down the file size of our CSS file from 185.76 KB to β yes that’s right β 2.63 KB.
Plugins
We could also use dependencies to pull in extra functionality into our starter theme. Think e.g. of SEO, performance, and security.
Wrap up
You’re now familiar with customizing build scripts. We also extended our example with Bootstrap, and optimized it for use in production. You’re also aware that you could use dependencies to pull in extra functionality.
You can find the full example in the npm-hugo repo I’ve set up.
In case youβre curious, the idea for this series was taken from a similar post written about WordPress.
Series posts
- Master npm with Hugo: an introduction to npm
- Master npm with Hugo: managing dependencies
- Master npm with Hugo: customizing build scripts