Hugo Add Customized CSS or Javascript

Recently I switched the static website generator from Hexo to Hugo. The main reason is that Hexo is too slow, cannot generate websites with thousands of pages.

Then I found this: # Who Should Use Hugo?

Hugo is for people building a blog, a company site, a portfolio site, documentation, a single landing page, or a website with thousands of pages.

No pain no gain. To use Hugo smoothly, the first problem is how to add customized css or js to the site.

The bad news is that Hugo is not that friendly to a freshman. I spent hours to read documents and understood how it works. Comparatively, Hexo's plugin system and injector is more friendly to a freshman (maybe I forgot how long to learn it haha :-D).

In this post, we will go through how to add customized css or js to Hugo sites in Hugoic way.

If you don't want to understand how it works, just go to Modify Templates section would be fine. :-)

Hexo Add Customized css/js

Add Customized css/js in Hexo is easy, just add a new file custom.js in the scripts folder of project root directory, then the js would be appended to the end of <head>:

hexo.extend.injector.register('head_end', () => {
  return '<script data-ad-client="ca-pub-xxx" async src=""></script>';
}, 'default');

Add Customized css/js In Hugoic Way

Simliar to pythonnic, what's the Hugoic way to inject customized css/js?

Hugo runs on top of theme templates, so we need to modify the templates to inject css/js. Before that, we should get some understanding of Hugo basics.

Hugo Layouts Lookup Rules With Theme

The first lesson:# Hugo Layouts Lookup Rules With Theme:

In Hugo, layouts can live in either the project’s or the themes' layout folders, and the most specific layout will be chosen. Hugo will interleave the lookups listed below, finding the most specific one either in the project or themes.

Unfortunately, I don't find the priority of project layout and theme layout explicitly. Here is an unofficial explanation: # Customizing a Theme

Customize the Templates

Based on Hugo layouts lookup rules, we should customize the templates by two steps:

  • Copy /themes/<yourtheme>/layouts/partials/xxx.html to the project root /layouts/partials/xxx.html, aka created a new file with the same folder structure of theme folder in the project root
  • Modify the new file /layouts/partials/xxx.html and add css/js code

The main advantages of the solution is the theme can be upgraded independent of the project repo. If we modify the theme files directly, there might be many conflicts when upgrade the theme.

The first time I read the above solution, I don't understand why it works. I spent hours to read documents and found that Hugo has many implicit rules. Here the rule would be the templates in the project layout will overwrite the templates in the theme layout.

layouts/_default/ holds many default templates, while layouts/partials holds partial templates:

Partials are smaller, context-aware components in your list and page templates that can be used economically to keep your templating DRY.

# Partial Template Lookup Order

However, partials are simpler in that Hugo will only check in two places:

  • layouts/partials/*<PARTIALNAME>.html
  • themes/<THEME>/layouts/partials/*<PARTIALNAME>.html

This allows a theme’s end user to copy a partial’s contents into a file of the same name for further customization.

Modify Templates

Here we take theme PaperMod as example, copy /themes/PaperMod/layouts/partials/head.html to /layouts/partials/head.html and append the following code to the file end:

{{ range .Site.Params.customcss }}
    <link rel="stylesheet" href="{{ . | absURL }}">
{{- end }}

{{ range .Site.Params.customjs }}
    <script type="text/javascript" src="{{ . | absURL }}"></script>
{{- end }}

{{ range $elem := .Site.Params.customjscssraw }}
    {{ $elem | safeHTML }}
{{- end }}

To fully understand the code, we need to read # Context and function usage of range, absURL and safeHTML in # Introduction to Hugo Templating.

Why do we need customjscssraw since we alreadly have customcss and customjs? Because sometimes add raw html is more flexible: add css integrity property, or add async keywords to js. This feature is comparable to Hexo injector. See the examples in the next section.

Add CSS or Js in the config.yml

The usage is pretty easy, just add the configuration like this (support muliti-line js as well):

    - "css/xx.css"
    - "js/xx.js"
    - "<link rel='stylesheet' href='' integrity='sha256-2H3fkXt6FEmrReK448mDVGKb3WW2ZZw35gI7vqHOE4Y=' crossorigin='anonymous'>"
    - >
      <script async src=''></script>
        window.dataLayer = window.dataLayer || [];
        function gtag(){dataLayer.push(arguments);}
        gtag('js', new Date());

        gtag('config', 'G-XXX');

Tips: Don't forget to add your css/js files to the static/ folder if their path are relative.

Reference: # Add Custom CSS Or Javascript To Your Hugo Site