For the online learning world

Elearning WorldTechnical

Static framework


In my previous posts about using Hugo we’ve been creating a statically generated website. In this post, we’ll look at using a ‘Framework’, in this case ‘Bootstrap’.

If you’ve not read my previous posts, then please do so that this one will make sense.


Names / logos can be trademarks of their respective owners. Please review their website for details.

I am independent from the organisations mentioned and am in no way writing for or endorsed by them.

The information presented in this article is written according to my own understanding, there could be technical inaccuracies, so please do undertake your own research.

The featured image is my copyright, please don’t use without my permission.


Bootstrap – getbootstrap.com

The why

When creating a website, you’ll want make it have the style that fits the design you want, to be able to have a structure, a layout and indeed reuse the same elements to provide consistency. This is what we’re doing with our theme. The thing is that everyone mostly has the same requirement, facing the same challenge. This is where a ‘Framework’ comes in. Its not a website theme in itself, but rather a collection of styles and components that you can then employ, and you’ve not had to do the work!

The chosen framework

Bootstrap is one such framework. I’ve chosen it simply because of familiarity. It has a grid system (getbootstrap.com/docs/5.3/layout/grid), typography (getbootstrap.com/docs/5.3/content/typography), cards (getbootstrap.com/docs/5.3/components/card), navigation bar (getbootstrap.com/docs/5.3/components/navbar)… in fact lots more to help and assist us.


To simplify things, I’ve downloaded the ‘off the shelf’ compiled minimised versions of the CSS and JavaScript (JS) from getbootstrap.com/docs/5.3/getting-started/download and placed them in the themes ‘assets’ folder (github.com/gjb2048/hugo-elw/tree/Feb_2024/themes/elw/assets). As they are global resources to be used everywhere, then we need to use the ‘Get’ function on ‘resources’ (gohugo.io/functions/resources/get) and additionally also use ‘Publish’ (gohugo.io/methods/resource/publish) to put the licence file in the ‘public’ folder when we publish the site for use on our web server, thus complying with its copyright statement and beyond what is already in the comments of the CSS and JS files. To keep things organised, there are two partial files, css.html and js.html.


This file contains:

<!-- Bootstrap -->
{{ $bootstrap := resources.Get "bootstrap/bootstrap.min.css" }}
{{ $bslic := resources.Get "bootstrap/Bootstrap_LICENSE.txt" }}
{{ $bslic.Publish }}

….. See full version on: https://github.com/gjb2048/hugo-elw/blob/Feb_2024/themes/elw/layouts/partials/css.html.

{{ $elwoptions := (dict "targetPath" "elw.css" "outputStyle" "compressed" "enableSourceMap" false) }}
{{ $elwstyle := resources.Get "sass/elw.scss" | resources.ExecuteAsTemplate "style.elw.scss" . | resources.ToCSS $elwoptions }}

{{ $elw := slice $bootstrap $brands $regular $solid $fa $elwfont $elwstyle | resources.Concat "assets/elw.css" }}
{{ if eq hugo.Environment "development" }}
    <link rel="stylesheet" href="{{ $elw.RelPermalink }}">
{{ else }}
  {{ with $elw | minify | fingerprint }}
    <link rel="stylesheet" href="{{ .RelPermalink | absURL }}" crossorigin="anonymous">
  {{ end }}
{{ end }}

which gets the Bootstrap CSS as a resource (does the licence publishing bit), then uses it in combination with other SCSS / CSS, including our own to produce one combined file to reduce the number of requests that the web browser has to make to the server. In this case, the development version isn’t further minified, but the production version is, along with being ‘fingerprinted’ (gohugo.io/hugo-pipes/fingerprint), this generates a ‘hash’ of the combined CSS that changes when any of the SCSS / CSS changes. This is then used in the generated filename, and so not have a browser cache issue when it does change. The browser will see it as a different file and fetch it.


This file contains:

<!-- Bootstrap -->
{{ $bsjs := resources.Get "bootstrap/bootstrap.min.js" | fingerprint }}
<script src="{{ $bsjs.Permalink | absURL }}" integrity="{{ $bsjs.Data.Integrity }}"></script>

{{ if eq hugo.Environment "development" }}
  {{ $opts := dict "targetPath" "assets/elw.js"}}
  {{ $elwjs := resources.Get "js/elw.js" | resources.ExecuteAsTemplate "elw.js" . | js.Build $opts }}
  {{ with $elwjs }}
    <script src="{{ .RelPermalink }}"></script>
  {{ end }}
{{ else }}
  {{ $opts := dict "minify" true "targetPath" "assets/elw.js"}}
  {{ $elwjs := resources.Get "js/elw.js" | resources.ExecuteAsTemplate "elw.js" . | js.Build $opts }}
  {{ with $elwjs | fingerprint }}
    <script src="{{ .RelPermalink | absURL }}" integrity="{{- .Data.Integrity }}" crossorigin="anonymous"></script>
  {{ end }}
{{ end }}

and it operates in terms of fingerprinting in the same way. However, the JS is kept separate simply because I’m not quite sure of the effects of concatenation at the moment (perhaps a future post). Again, there is a difference between development and production environments for the themes JavaScript. This is to have a non-minified version of our JS to aid in debugging.

You’ll notice the ‘integrity’ HTML attribute, this is there to ensure that the script has not been tampered with (www.w3schools.com/tags/att_script_integrity.asp). It’s generated by Hugo as a part of the fingerprint process (gohugo.io/hugo-pipes/fingerprint).


Now that we have partials that get the Bootstrap CSS and JS, we need to incorporate them on our web page. But where? The CSS is used to style the page and thus its rules are needed to be known to render the markup, so it goes in the ‘head’ of the markup. But what about the JS? In thinking and researching for this post, I had in the back of my mind that the JS should actually be placed towards the end of the markup. A search of the web finds that this is the case but I couldn’t find a really definitive reference. However, it goes something like this. That JS is at the end of the markup as its not needed to be processed until after the markup is fully parsed and understood. Having the browser spend time getting the JS and processing it, takes up time which initially is better spent rendering the page and presenting it to the user. Then after that the JS can be applied. Of course, that would depend on what the JS actually does though.

Given this, then our ‘head.html’ partial has:

{{ partialCached "css.html" . }}

and our ‘foot.html’ partial has:

{{ partialCached "js.html" . }}

then our ‘baseof.html’ layout (gohugo.io/templates/base) then includes them:

<!DOCTYPE html>
<html lang="{{ or site.Language.LanguageCode site.Language.Lang }}" dir="{{ or site.Language.LanguageDirection `ltr` }}">
    {{ partial "head.html" . }}
    {{ partial "navbar" . }}
      {{ partial "header.html" . }}
      {{ block "main" . }}{{ end }}
      {{ partial "footer.html" . }}
    {{ partial "foot.html" . }}

Now we are in a position to use the framework, which we do in the ‘main’ block, for example on a ‘list’ page (‘layouts/list.html’ – gohugo.io/templates/lists) we have:

{{ define "main" }}
  <main class="text-center text-md-start">
    <div class="row">
        <h1 class="col-12">{{ .Title }}</h1>
        <div class="col-12">{{ .Content }}</div>
    <div class="row row-cols-1 row-cols-sm-2 row-cols-md-3">
    {{ range .Pages }}
        {{ partial "list-item" . }}
    {{ end }}
{{ end }}

which includes the partial ‘list-item’:

{{ .Date.Format (.Site.Params.dateFormat | default "Monday, 2 January 2006") | .Scratch.Set "subheader" }}
<div class="col mb-3">
    <div class="card h-100">
        <div class="card-body">
            <h2 class="card-title"><a href="{{ .RelPermalink }}" target="_self">{{ .Title }}</a></h2>
            <div class="card-text">
                {{ with .Description }}
                <p>{{ . }}</p>
                {{ end}}
                {{ with .Params.summary}}
                <p class="small">{{ . }}</p>
                {{ end }}
            {{ with .Resources.GetMatch "thumbnail" }}
            <img class="w-100" src="{{ .Permalink }}" title="{{ .Title }}" alt="{{ .Params.alt }}">
            {{ end }}
        <div class="card-footer">
            <p>{{ .Scratch.Get "subheader" }}</p>
            {{ if .Params.tags }}
            <div class="item-tags">
                {{ partial "tags" . }}
            {{ end}}


List template output

where I’ve employed the CSS classes to create a responsive card based layout where each card represents a post.

To understand Bootstrap and how it can be employed within your markup, please have a look at the examples getbootstrap.com/docs/5.3/examples and documentation getbootstrap.com/docs/5.3.


In the next few posts about Hugo, I intend to look at render hooks and using different languages.


I hope that you’ve been able to understand how to integrate a framework into your Hugo site.

You can get all of the code for this post from: github.com/gjb2048/hugo-elw/tree/Feb_2024.

What do you think please?

Gareth Barnard
Latest posts by Gareth Barnard (see all)

Gareth Barnard

Gareth is a developer of numerous Moodle Themes including Essential (the most popular Moodle Theme ever), Foundation, and other plugins such as course formats, including Collapsed Topics.

One thought on “Static framework

  • Anonymous

    Nice post !
    Thanks for including the screenshot at the end, which helped me go back and improve my understanding of the previous code 🙂


Add a reply or comment...