Converting Simple Web Page Into Hugo Theme Part 4 - Externalized Configuration

February 11, 2019

Externalizing theme configuration

Everything so far looks OK but navigation is hardcoded in sidebar but it should be configurable so that one can enable/disable/change some options.

Hugo pulls site configuration from two places, global site config file (blogv1/config.toml) and theme config file (blogv1/themes/only-bones/config.yaml). Some properties from site config file cannot be overriden by theme config file, more on that here.

Notice different files extensions of the config files. It doesn’t matter as Hugo supports toml, yaml and json format (BTW, you can convert between these configurations (and see what changes) using toolkit.site).

<nav class="main-nav">
  <h3>{{ .Site.Title }}</h3>

  <ul class="nav">
    {{ range .Site.Menus.main }}
    <li>
      <a href="{{ .URL }}">
        {{ .Pre }}
        <span>{{ .Name }}</span>
      </a>
    </li>
    {{ end }}
  </ul>

  <ul class="social">
    {{ range .Site.Params.social }}

    {{ if .enabled }}
    <li>
      <a href="{{ .url }}" {{ if ne .name "email" }} target="_blank" rel="noopener" {{ end }} >
        {{ .name }}
      </a>
    </li>
    {{ end }}

    {{ end }}
  </ul>
</nav>

What we did here is that we’ll populate nav menu and social icons from configuration file so that they can be changes when needed.

Our blogv1/config.toml that will populate nav menu looks like this:

[menu]

  [[menu.main]]
  identifier = "blog"
  name = "Blog"
  title = "Blog"
  url = "/posts/"
  weight = -110.0

  [[menu.main]]
  identifier = "about"
  name = "About"
  title = "About"
  url = "/about/"
  weight = -109.0

[params]

  [[params.social]]
  name = "linkedin"
  url = ""
  enabled = true

  [[params.social]]
  name = "email"
  url = "mailto:"
  enabled = true

  [[params.social]]
  name = "twitter"
  url = ""
  enabled = false

  [[params.social]]
  name = "github"
  url = ""
  enabled = false

  [[params.social]]
  name = "gitlab"
  url = ""
  enabled = false

  [[params.social]]
  name = "rss"
  url = "/index.xml"
  enabled = true

  [analytics]
  id = "UA-111111111-2"
  enabled = true

menu configuration is accessed using Hugos .Site.Menu struct and social configuration using .Site.Params struct.

Well use analtyics settings to populate partials/footer.html:

{{ with .Site.Params.analytics}}
{{ if index . "enabled"}}
<footer>
<script async src="https://www.googletagmanager.com/gtag/js?id={{ index . "id" }}"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', '{{ index . "id" }}');
</script>
</footer>
{{ end }}
{{ end }}

Hugos with function automatically handles case when a config is not defined, it’ll simply skip the enclosing block. If config is defined it’ll set parsed config object into context so that its properties can be easily accessed using . notation.

Read more about with here.

In this case [analytics] block from config is parsed into ordinary Go map whose entries can be accessed using index function. More on index here.

With footer defined, include it into baseof.html layout so that it appears on every page:

<!DOCTYPE html>
<html>

{{- partial "header" . -}}

<body>
  <div class="wrapper">

    {{- partial "sidebar" . -}}

    {{- block "main" . }}{{- end }}

  </div>
</body>

{{- partial "footer" . -}}

</html>

In the next part, we’ll see how to use Hugo’s templating to conveniently add some icons for social and nav buttons.