How to customize scripts with npm — for Hugo

How to customize scripts with npm — for Hugo

This is the third post of the three piece series Using npm with Hugo.

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.

Let's continue.

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:
    npm run clean
    hugo server --disableFastRender
    echo "Error: no test specified" && exit 1

available via `npm run-script`:
    npm run clean
    hugo --gc --minify
    shx rm -rf public resources

Running scripts

The npm run start command e.g. will:

  1. delete the temporary build directories, and next
  2. start the hugo server with the --disableFastRender flag enabled.


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:

    {{ $options := (dict "targetPath" "main.css" "includePaths" (slice "node_modules")) -}} {{ $css :=
    resources.Get "app.scss" | resources.ToCSS $options -}}
    <link rel="stylesheet" href="{{ $css.Permalink }}" />

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>
{{ 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: [
            content: ['./layouts/**/*.html']

Update ./layouts/partials/head.html:

    {{ $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 }}" />

Update ./config.toml:

baseURL = "/"

Add to package.json:

  "browserslist": [
  • http-server is a simple, zero-configuration command-line http server — handy for viewing a local production build.
  • Chrome DevTools is a set of web developer tools built directly into the Google Chrome browser.

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.


We could also use dependencies to pull in extra functionality into our starter theme. Think of e.g. SEO, performance, and security.

Wrapping 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 now also aware that you can 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.