JustToThePoint English Website Version
JustToThePoint en español
JustToThePoint in Thai

A complete hands-on guide to using Hugo

Let’s get started.


Hugo is one of the most popular open-source static site generators written in Go. It is fast, ease of use, and flexible. Hugo supports markdown JustToThePoint, Markdown and LaTeX.

Programming in Hugo

Layouts in Hugo and basic structure


A layout is a page used as a “frame”, “skeleton” or “basic template” to display your content. It consists of all the parts that remain consistent across all of the pages. Hugo uses a layout for the home page, a second one for content pages, and a third one that shows or displays lists of pages.

The main home page layout will live in themes/anawim/layouts/index.html, where anawim is my theme. This file defines the HTML structure or skeleton for the site’s home page:

{{ define "main" }}
<div class="container mx-auto">
    <h1> {{ .Site.Title }} </h1>

{{ .Site.Title }} specifies that we want to pull the title field out of the config.yaml, the main Hugo’s configuration file.

    {{if .Params.subtitle }}
    <h2 class="text-secondary h3"> {{ .Params.subtitle }} </h2>    
    {{ end }}
    <p> {{ .Description }} </p>
    {{ .Content | markdownify }}
</div>

The {{ .Content | markdownify }} line displays the content of the page. This content will be in a Markdown document in our content folder, more specifically, in the “scope” or context" that contains the data we want to access. The current context is represented by a dot “.” and the content’s file is a file named _index.md in the content directory. It takes this content and runs it through Hugo’s Markdown processor.

The layouts directory contains all the HTML files that are used for generating HTML from the Markdown files. In other words, they specify how your content will be rendered. The _default directory is the place where Hugo will search for the base page layout file, that is, baseof.html:

<!doctype html>
<!-- prefix="og: http://ogp.me/ns#" is the required prefix for open graph protocol meta tags. -->
<html prefix="og: http://ogp.me/ns#" lang="{{ .Site.Language.Lang }}" >
  <!-- Schema.org provides a collection of shared vocabularies webmasters can use to mark up their pages 
  in ways that can be understood by the major search engines! -->
  <head itemscope itemtype="https://schema.org/WebPage">
    {{- partial "mymeta" . -}}   <!-- SEO: meta tags -->
    {{- partial "head" . -}} <!-- Google Analytics -->
  </head>
  <body>
    {{- partial "search-form" . }} <!-- Search with Lunr -->
    {{- partial "navbar" . -}} <!-- Menu, Navbar -->
    {{- block "main" . -}} {{ end }}  
    {{- partial "script-footer" . -}} <!-- Javascript -->
    {{- partial "footer" . -}} <!-- Social Media, Copyright, Privacy Policy and Terms of Use -->
    {{- partial "style" . -}} <!-- Css -->
    {{- partial "search-index.html" . }} <!-- Search with Lunr -->
  </body>
</html>

Observe that we define blocks and partial where we define the different “sections” or “pieces” of our content, e.g., the first two partials (mymeta, head) contain the code that will appear in the HEAD. The dash “-” removes whitespace characters in the output, so we are using it in every partial to reduce the number of blank lines that Hugo generates.

Under the layouts folder, you can see there is a folder called partials. It contains all our partial templates.

There is a “main” block layout. This line looks for this block and pulls its content. It is defined in index.html (site’s home page) and single.html.

list.html is the “frame” or “html template” that Hugo uses to list posts of your site under a particular directory. single.html is the templated used to display a single page.

If you want to write about shortcodes, you need to replace the opening greater-than sign “<” with &lt; or “%” with &#37;.

{{ < img src="../images/best-friends.webp" title=“best-friends” src2="../images/best-friends.jpg" >}}

By default, Hugo replaces two hyphens for a single dash, “–” the solution is this: &hyphen;&hyphen; ‐‐

The same goes with emojis, instead of :star: ⭐️, you use an HTML entity for one of the colons in the emoji code: :star&#58;

If you want that Hugo does not turn &hyphen; into “‐”, you need to replace the ampersand “&” with &amp; e.g., content_new = pattern.sub('&amp;hyphen;&amp;hyphen;', content_new)

 content_new = pattern.sub("&hyphen;&hyphen;", content_new)

I also use this shortcode -spam.html-, htmlEscape returns the given string with the reserved HTML coded escaped:

<span {{ with .Get "style"}} style="{{ . | safeCSS }}"{{ end }}>{{ .Get "text" | htmlEscape }}</span>

First, we use the .Get function to retrieve the style parameter passed to our shortcode (e.g., “color:red;”). The dot’s value is different from the value inside the “with” block, it changes based on context, which is our style in this case. safeCSS declared the provided string as a “safe” CSS string. Next, we retrieve the text parameter and pipe it into htmlEscape.

Use:

{{< span style="color:red;" text="&hyphen;" >}}

Result: &hyphen;

This is a primary alert.

Basic configuration.


Let’s open config.yaml:

baseURL: https://justtothepoint.com/ # url address of the website | required
theme: "anawim"
pygmentsUseClasses: true # We are going to use a style sheet for highlighting. 
pygmentsCodefences: true # It enable syntax highlighting in code fences with a language tag (html, python, css, json, md, etc.) in markdown: ``` html [ my code html ] ``` 
PygmentsStyle: "monokai"
languageCode: en-us
title: JustToThePoint  # The title of the site.
copyright: "Copyright © 2022 JustToThePoint. All rights reserved." # Copyright notice for your site, typically displayed in the footer.
googleAnalytics: YourCodeHere # It enables Google Analytics by providing your tracking ID.
defaultContentLanguage: en # Content without language indicator will default to this language.
enableEmoji: true # It enables Emoji emoticons support.
enableRobotsTXT: true # It enables generation of robots.txt file. It tells search engine crawlers which URLs the crawler can access on your site.
assetDir: "assets/"

Without adding a single line to our config file, Hugo will automatically create taxonomies for tags and categories. I don’t want Hugo to create any taxonomies, so I set disableKinds in config.yaml as follows:

disableKinds:
- taxonomy
- term
markup: # Configure Markup
    goldmark: # Goldmark is the default library used for Markdown.
        renderer:
            unsafe: true # By default, Goldmark does not render raw HTML.
    highlight: # This is the highlight configuration. Hugo uses Chroma as its code highlighter.
        codeFences: true # It enables syntax highlighting with GitHub flavored code fences: ``` html [ my code html ] ``` By default, Highlighting is carried out via the built-in shortcode highlight.
        guessSyntax: true # It tries to do syntax highlighting on code fenced blocks in markdown without a language tag.
        lineNos: false # It configures line numbers.
        tabWidth: 4
            
params:
    author: Máximo Núñez Alarcón, Anawim
    og_image: https://www.justtothepoint.com/myImages/logotipoMagotipoCabecera.png
    description: Free bilingual e-books, articles, resources, and videos to help your child and your entire family succeed, develop a healthy lifestyle, and have a lot of fun.

params:
    social:
        profiles: 
            facebook: "https://www.facebook.com/nmaximo7"
            github: "https://github.com/nmaximo7"
            twitter: "http://Twitter.com/justtothepoint"
            instagram: "http://instagram.com/justtothepoint"
            linkedin: "https://www.linkedin.com/in/justtothepoint"
            youtube: "https://youtube.com/justtothepoint"
  

Let’s use NPM as a Build Tool with Hugo


NPM is a package manager for JavaScript. It consists of a command line client (npm) and an online database of public and paid-for private packages, called the npm registry.

Node.js (Node) is a run time open source development platform. It allows us to write JavaScript code that runs directly in a computer process instead of in a browser, so it is used to write server-side applications with access to the operating system, file system, and everything else required to build fully-functional applications.

  1. Initialize the project: npm init (project’s name, initial version, description, etc.) It will generate a package.json file in the current directory. A package.json file is your project’s manifest. It includes the packages and applications it depends on, information about its unique source control, and specific metadata.

  2. You can install modules with npm install myModule or install modules and save them to your package.json as a dependency: npm install –save-dev npm-check. We are going to install npm-check and hugo-installer.

    npm-check checks for outdated, incorrect, and unused dependencies. hugo-installer installs Hugo into your repository: npm install hugo-installer –save-dev

  3. Edit the package.json file. JSON does not support comments.

{
  {
  "name": "justtothepoint", // The name of the project.
  "version": "1.0.0", // The version of the project.
  "description": "Free resources, bilingual e-books and videos to help your child and your entire family succeed, develop a healthy lifestyle, and have a lot of fun.", // The description of the project
  "main": "index.js",
  "scripts": {
    "dev": "exec-bin node_modules/.bin/hugo/hugo server --gc --disableFastRender", 
    // It runs Hugo's webserver: -gc remove unused cache files after the build; --disableFastRender enables full re-renders on changes.
    "build": "exec-bin node_modules/.bin/hugo/hugo --gc -cleanDestinationDir --minify", 
    // --cleanDestinationDir removes files from destination not found in static directories. --minify reduces CSS code size and make your website load faster.
    "prebuild": "exec-bin node_modules/.bin/hugo/hugo --gc --cleanDestinationDir",
    "npm-check": "npm-check -u",
    "start": "exec-bin node_modules/.bin/hugo/hugo server -d-disableFastRender --gc --renderToDisk --cleanDestinationDir", 
    // --renderToDisk serve all files from disk. Please notice that the default is to serve all files from memory.
    "postinstall": "hugo-installer --version otherDependencies.hugo --extended --destination node_modules/.bin/hugo"
    // hugo-installer is recommended as part of our postinstall hook within our project's package.json. It installs Hugo into the repository. --destination is the path to the folder into which the binary will be put. --extended download the extended version of Hugo.
  },
  "author": "Máximo Núñez Alarcón, Anawim", // The author of the project.
  "license": "ISC", // The license of the project.
  "devDependencies": {
    "npm-check": "^5.9.2"
  },
  "dependencies": {
    "exec-bin": "^1.0.0",
    "hugo-installer": "^3.1.0"
  },
  "otherDependencies": {
    "hugo": "0.96.0"
  }
}

SEO with Hugo: mymeta.html


We are going to add all our meta tags under the mymeta partial. It is one of the partials included in the baseof.html file. You may want to read the article Perfect SEO Meta Tags with Hugo, SKCRIPT:

<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>{{ .Title }}{{ if ne .Title .Site.Title }} | {{ .Site.Title }}{{ end }}</title>
<meta name="application-name" content="{{ .Title }}{{ if ne .Title .Site.Title }} | {{ .Site.Title }}{{ end }}" />
<meta itemprop="name" content="{{ .Title }}{{ if ne .Title .Site.Title }} | {{ .Site.Title }}{{ end }}" />
<meta name="description" content="{{ if .Params.Description }}{{ .Params.Description }}
{{ else if .Site.Params.Description }}{{ .Site.Params.Description }}
{{ else }}Free bilingual e-books, articles, resources, and videos to help your child and your entire family succeed, develop a healthy lifestyle, and have a lot of fun.{{ end }}" /> 
<meta itemprop="description" content="{{ if .Params.Description }}{{ .Params.Description }}
{{ else if .Site.Params.Description }}{{ .Site.Params.Description }}
{{ else }}Free bilingual e-books, articles, resources, and videos to help your child and your entire family succeed, develop a healthy lifestyle, and have a lot of fun.{{ end }}" />
<meta name="keywords" content="{{with .Params.Keywords }}{{ delimit . " "}}{{ end }}" />
<meta name="language" content="{{ if .Params.Language }}{{ .Params.Language }}{{ else }}en_US{{ end }}" />

<!-- Open Graph Tags -->
<meta property="og:title" content="{{ .Title }}{{ if ne .Title .Site.Title }} | {{ .Site.Title }}{{ end }}" />
<meta property="og:locale" content="{{ if .Params.Language }}{{ .Params.Language }}{{ else }}en_US{{ end }}" />
<meta property="og:site_name" content="{{ .Site.Title }}" />
<meta property="og:url" content="{{ .Permalink }}" />
<meta property="og:description" content="{{ if .Params.Description }}{{ .Params.Description }}
{{ else if .Site.Params.Description }}{{ .Site.Params.Description }}
{{ else }}Free bilingual e-books, articles, resources, and videos to help your child and your entire family succeed, develop a healthy lifestyle, and have a lot of fun.{{ end }}" />
<meta property="og:article:author" content="https://www.facebook.com/nmaximo7" />

<!-- Twitter Tags -->
<meta name="twitter:title" content="{{ .Title }}{{ if ne .Title .Site.Title }} | {{ .Site.Title }}{{ end }}" />
<meta name="twitter:description" content="{{ if .Params.Description }}{{ .Params.Description }}
{{ else if .Site.Params.Description }}{{ .Site.Params.Description }}
{{ else }}Free bilingual e-books, articles, resources, and videos to help your child and your entire family succeed, develop a healthy lifestyle, and have a lot of fun.{{ end }}" />
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@justtothepoint" />
<meta name="twitter:creator" content="@justtothepoint" />
<meta name="twitter:domain" content="https://www.justtothepoint.com/" />
<meta name="twitter:url" content="{{ .Permalink }}" />

<!-- Link Tags -->
<base href="{{ .Permalink }}" />
<!-- A canonical URL is the URL of the page that Google thinks is most representative 
	from a set of duplicate pages on your site.  -->
<link rel="canonical" href="{{ .Permalink }}" itemprop="url" />
<meta name="url" content="{{ .Permalink }}" />

<!-- Image Tag -->
{{ with .Params.featured_image }}
  <meta itemprop="image" content="{{ . | absURL }}" />
  <meta property="og:image" content="{{ . | absURL }}" />
  <meta name="twitter:image" content="{{ . | absURL }}" />
  <meta name="twitter:image:src" content="{{ . | absURL }}" />
{{ else }}
  <meta itemprop="image" content="{{ .Site.Params.ogimage | absURL }}" />
  <meta property="og:image" content="{{ .Site.Params.ogimage | absURL }}" />
  <meta name="twitter:image" content="{{ .Site.Params.ogimage | absURL }}" />
  <meta name="twitter:image:src" content="{{ .Site.Params.ogimage | absURL }}" />
{{ end }}

RealFaviconGenerator is a favicon generator for all platforms and browsers. After downloading the generated favicon package, unzip it, and copy all its files into the Hugo’s static folder (/static).

<!-- Favicon Tags -->
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
<meta name="msapplication-TileColor" content="#AADFE1">

<!-- It indicates a suggested color that user agents should use to customize the display of the page. ColorZilla is a tool picker that could be useful for that purpose. -->
<meta name="theme-color" content="#AADFE1">

<meta name="google-site-verification" content="YourCodeHere" />
<meta name="msvalidate.01" content="YourCodeHere" />
<meta name="p:domain_verify" content="YourCodeHere" />
<meta name="yandex-verification" content="YourCodeHere" />
<meta property="article:publisher" content="https://www.facebook.com/nmaximo7" />
<meta property="article:author" content="https://www.facebook.com/nmaximo7" />
<meta property="fb:app_id" content="YourCodeHere" />


<!-- Sitemap and RSS -->
<link rel="sitemap" type="application/xml" title="Sitemap" href='{{ "sitemap.xml" | absURL }}' />
{{ with .OutputFormats.Get "RSS" }}
	<link href="{{ .Permalink }}" rel="alternate" type="application/rss+xml" title="{{ $.Site.Title }}" />
	<link href="{{ .Permalink }}" rel="feed" type="application/rss+xml" title="{{ $.Site.Title }}" />
{{ end }}

<!-- Search Engine Crawler Tags -->
<meta name="robots" content="index,follow" />
<meta name="googlebot" content="index,follow" />

To add Twitter card and Open Graph metadata, you can use two internal templates (opengraph, twitter_cards) by including the following lines between the HEAD tags in your templates:

{{ template "_internal/opengraph.html" . }}
{{ template "_internal/twitter_cards.html" . }}

head.html and Google Analytics


Firstly, you need to enable Google Analytics by editing Hugo’s configuration file (config.yaml) and add the following code: googleAnalytics = “Add Your Google Analytics code”

Then, we will use a Hugo template (_internal/google_analytics_async.html) to enable Google Analytics on our site. It will be written on the head.html’s last line. When Hugo generates your site, the appropriate JavaScript code will be added to your pages.

    <!-- Google Analytics. Credits: JANNE KEMPPAINEN, https://pakstech.com/, Let's Create a New Hugo Theme. -->
    {{ if not (in (string .Site.BaseURL) "localhost") }}
    <!-- We don't want Google Analytics when we are deploying Hugo locally (localhost) -->
        {{ template "_internal/google_analytics_async.html" . }}
    {{ end }} 

Styling. Bootstrap. Minify and bundle assets


The idea is to use Hugo’s asset pipeline to bundle all the css into a single one (css/bundle.css). Next, we minify it (it removes whitespace, strips comments, etc.) to reduce CSS code size and make our website load faster.

I will put it in layout/partials/style.html that is referenced in _default/baseof.html.

{{ $csBundle := slice }}
<!-- Bootstrap CSS.  -->
{{ $csBundle := $csBundle | append (resources.Get "css/bootstrap.min.css") }}

We are going to build our website using Bootstrap, a free and open-source CSS framework so we can design and customize responsive mobile-first websites.

Our styles will live in a new directory, the assets folder, under a “css” directory. The resources.Get functions uses the assets directory as its base path.

<!-- My main CSS  -->
{{ $csBundle := $csBundle | append (resources.Get "css/main.css") }}
<!-- Monokai CSS. hugo gen chromastyles --style=monokai > syntax.css. -->
{{ $csBundle := $csBundle | append (resources.Get "css/syntax.css") }}
<!-- Awesome CSS Fonts. -->
{{ $csBundle := $csBundle | append (resources.Get "css/all.min.css") }}

{{ $style := $csBundle | resources.Concat "css/bundle.css" | minify | fingerprint }}

<link rel="stylesheet"
      href="{{ $style.Permalink | absURL }}"

Hugo provides a fingerprint module to generate hashing for cache busting. It will update your css’s .Permalink with a hash.

Cache Busting is the process of appending some query parameter in the URLs of static resources such as JS, CSS or image file. It is useful because it allows your visitors to receive the most recently updated files without having to perform a hard refresh or clean their browser cache. Otherwise, your users might not notice the changes unless they clear their local caches.

JavaScript


There is no much to say here. We will bundle, minify, and fingerprint our Javascript files. They will live under the assets directory in a js folder. I will put it in layout/partials/scrip-footer.html that is referenced in _default/baseof.html.

{{ $jsBundle := slice }}
<!-- Bootstrap Js  -->
{{ $jsBundle := $jsBundle | append (resources.Get "js/bootstrap.min.js") }}

<!-- My main Js  -->
{{ $jsBundle := $jsBundle | append (resources.Get "js/main.js") }}

{{ $globalJS := $jsBundle | resources.Concat "js/global.js" | minify | fingerprint }}

<script src="{{ $globalJS.Permalink }}">script>

The footer is a section of the site that appears at the very bottom of every page. It typically contains a copyright notice and links to your privacy policy, terms of use, and social media. To reduce maintenance, we are going to use Hugo’s configuration file, config.yaml, to place these links and information:

params:
    author: PhD. Máximo Núñez Alarcón, Anawim
    og_image: https://www.justtothepoint.com/myImages/logotipoMagotipoCabecera.png
    description: Free bilingual e-books, articles, resources, and videos to help your child and your entire family succeed, develop a healthy lifestyle, and have a lot of fun.
    social:
        profiles: 
            facebook: "https://www.facebook.com/nmaximo7"
            github: "https://github.com/nmaximo7"
            twitter: "http://Twitter.com/justtothepoint"
            instagram: "http://instagram.com/justtothepoint"
            linkedin: "https://www.linkedin.com/in/justtothepoint"
            youtube: "https://youtube.com/justtothepoint"
        share:
            facebook: true
            linkedin: true
            twitter: true
            whatsapp: true
            email: true
            line: true

    # My footer
    websiteTermsEs: "/contactes/terminos-y-condiciones-de-uso/"
    privacyPolicyEs: "/contactes/politica-de-cookies/"
    websiteTermsEn: "/contact/website-terms-and-conditions-of-use/"
    privacyPolicyEn: "/contact/cookies-policy/"
    copyrightStart: "2011"

    # Related posts. Hugo provides a sensible default configuration of Related Content, but you can fine-tune it in Hugo's configuration file.
    related:
      threshold: 70
      includeNewer: true
      toLower: false
      indices:
      - name : "keywords"
        weight: 150
      - name: "categories"
        weight: 80
      - name : "date"
        weight: 10
        pattern: "2006"

Next, we will create a new partial under layout/partials/footer.html that is referenced in _default/baseof.html:

<div class="site-info">
    <div class="site-info-container">
       <center>
        <img src="{{ "myImages/nenes2.png" | absURL }}" width="60%" ></center>
        <p><a href="https://justtothepoint.com/">JustToThePoint</a> Copyright &copy; {{ .Site.Params.copyrightStart }} - {{ dateFormat "2006" now }} {{ .Site.Params.author }}. ALL RIGHTS RESERVED. Bilingual e-books, articles, and videos to help your child and your entire family succeed, develop a healthy lifestyle, and have a lot of fun.</p>

Let’s add Copyright information to our footer. We are adding {{ dateFormat "2006" now }}, so we don’t need to update our copyright info every year. now returns the current local time. We use the .Format function to display only the current year.

       {{ range $key, $url := site.Params.social.profiles }}

Please observe that Hugo variables are always prefixed with a $sign. To look through a list of items, we use the Range function. We are initializing two variables at once, a key-value pair, e.g., $key (facebook) and $url (https://www.facebook.com/nmaximo7).

            {{ if eq $key "facebook"}}

Observe that if is the condition, and eq is a function. It compares the first parameter ($key) with the following one, in this particular case, facebook. If the key is Facebook, then we will show the corresponding icon (we are using Awesome fonts) and href will point to our Facebook link (it is provided in config.yaml).

                <a class="btn btn-default" href="{{ $url }}"><i class="fab fa-facebook fa-2x" ></i></a>
            {{ else if eq $key "twitter" }}
                <a class="btn btn-default" href="{{ $url }}"><i class="fab fa-twitter fa-2x" ></i></a>
            {{ else if eq $key "github" }}
                <a class="btn btn-default" href="{{ $url }}"><i class="fab fa-github fa-2x" ></i></a>
            {{ else if eq $key "instagram" }}
                <a class="btn btn-default" href="{{ $url }}"><i class="fab fa-instagram fa-2x" ></i></a>
            {{ else if eq $key "youtube" }}
                <a class="btn btn-default" href="{{ $url }}"><i class="fab fa-youtube fa-2x" ></i></a>
            {{ else if eq $key "linkedin" }}
                <a class="btn btn-default" href="{{ $url }}"><i class="fab fa-linkedin fa-2x" ></i></a>
            {{ end }}   
        {{ end }}       
        {{ if eq (string .Params.language) "es" }}      
            <p>Esta web utiliza 'cookies' propias y de terceros para ofrecerte una mejor experiencia y servicio. <br>Al navegar o utilizar nuestros servicios, estas aceptando nuestra <a href="{{ .Site.Params.privacyPolicyEs }}">Política de Cookies</a>, así como, nuestros <a href="{{ .Site.Params.websiteTermsEs }}">Términos y condiciones de uso.</a></p>
        {{ else }} 
            <p>This website uses cookies to improve your navigation experience.<br> By continuing, you are consenting to our use of cookies, in accordance with our <a href="{{ .Site.Params.privacyPolicyEn }}">Cookies Policy</a> and <a href="{{ .Site.Params.websiteTermsEn }}">Website Terms and Conditions of use.</a></p>
        {{ end }}
       
   </div> <!-- .site-info-container -->
</div><!-- .site-info -->

We are going to create another partial to show our social icons and share our links on social media. You can copy the below code snippet as a reference and save it under layout/partials/socialshare.html:

<!-- Social Share Button HTML -->
<style> <!-- We can add a separate .css file to Hugo, but we can also add an internal stylesheet. Adapt your own CSS style based on your theme -->
  .myButtonShare  {
  padding: 0;
  border: 0;
  background: transparent;
}
 .myImageShare {
  display: block;
}
.containerBitcoin {
    background: url( "/myImages/myBitcoin.png" ) no-repeat;
    display: inline-block;
    background-size: 100px 100px; /* image's size */
    height: 100px; /* image's height */
    padding-left: 100px; 
}

.containerBitcoin span {
    height: 100px; /* image's height */
    display: table-cell;
    vertical-align: middle;
}

</style>
<section class="social-share">
  {{ $title := .Title }}
  {{ $url := printf "%s" .Permalink }}
  {{ $body := print .Site.Title " " .Params.description }}
  <!-- We create and initialize three variables: title, url, and body. Observe that we use "printf" to format our URL (.Permalink), and "print" to concatenate the Site's title with the page's description. -->
  <!-- Twitter -->
  {{ if .Site.Params.social.share.twitter }}

We have added: params, social, share, twitter: true in our main Hugo’s configuration file, config.yaml, so we can enable and disable our social media icons and links site-wide.

  <a href="https://twitter.com/intent/tweet?hashtags=justtothepoint&amp;url={{ .Permalink }}&amp;text={{ $body }}&amp;" target="_blank" rel="noopener" aria-label="Share on Twitter">
    <button class="myButtonShare" >
      <img class="myImageShare" src="/myImages/twitter.png">
    </button>
  </a>
  {{ end }}
  <!-- Facebook -->
  {{ if .Site.Params.social.share.facebook }}
  <a href="https://facebook.com/sharer/sharer.php?u={{ .Permalink }}" target="_blank" rel="noopener" aria-label="Share on Facebook">
    <button class="myButtonShare" >
      <img class="myImageShare" src="/myImages/facebook.png">
    </button>
  </a>
  {{ end }}

  <!-- LinkedIn -->
  {{ if .Site.Params.social.share.linkedin }}
  <a href="https://www.linkedin.com/shareArticle?mini=true&amp;url={{ $url }}&amp;source={{ $url }}&amp;title={{ $title }}&amp;summary={{ $title }}" target="_blank" rel="noopener" aria-label="Share on LinkedIn">
    <button class="myButtonShare" >
      <img class="myImageShare" src="/myImages/linked-in.png">
    </button>
  </a>
  {{ end }}

  <!-- WhatsApp -->
  {{ if .Site.Params.social.share.whatsapp }}
  <a href="whatsapp://send?text={{ $body }}-{{ $url }}" target="_blank" rel="noopener" aria-label="Share on WhatsApp">
    <button class="myButtonShare">
      <img class="myImageShare" src="/myImages/whatsapp.png">
    </button>
  </a>
  {{ end }}

  <!-- Email -->
  {{ if .Site.Params.social.share.email }}
  <a href="mailto:?subject={{ .Site.Title }} - {{ $title }}.&amp;body={{ $body }} - {{ $url }}" target="_self" rel="noopener" aria-label="Share by E-Mail">
    <button class="myButtonShare">
      <img class="myImageShare" src="/myImages/email.png">
    </button>
  </a>
  {{ end }} 

  <!-- Line -->
  {{ if .Site.Params.social.share.line }}
  <a href="https://social-plugins.line.me/lineit/share?url={{ $url }}" target="_blank" rel="noopener" aria-label="Share on Line">
    <button class="myButtonShare">
      <img class="myImageShare" src="/myImages/line.png">
    </button>
  </a>
  {{ end }} 

  <!-- Buy me a coffee -->
  <a href="https://buymeacoffee.com/justtothepoint" target="_blank" rel="noopener">
    <button class="myButtonShare">
      <img class="myImageShare" src="/myImages/buyMeCoffee.png">
    </button>
  </a>

Hugo supports a solution for related content Hugo, Related Content out of the box.

<!-- Related Posts -->
<div id="relatedposts">
<h2>Related Posts</h2>
{{ $related := .Site.RegularPages.Related . | first 5 }}
{{ with $related }}
<ul>
  {{ range . }}
  <li>
    <a href="{{ .Permalink }}">
      <img src="{{ .Params.featured_image | absURL  }}" alt="{{ .Title }}"/>
      <p>{{ .Title }}</p></a>
  </li>
  {{ end }}
</ul>
{{ end }}
</div>

Finally, we have added our socialshare partial to the single.html file:

{{- define "main" }}
[....]
{{ partial "socialshare.html" . }}
{{- end }}

Creating and using data with Hugo


Even though Hugo is a static site generator, i.e., a tool that generates a full static HTML website based on HTML templates and Markdown content, we can still have access and pull data from external sources, such as APIs or local data files.

First, let’s create a folder called data in the root of your project and a file “quotes.json” in it with the following content:

[
    {
        "author": "Douglas Adams, The Hichhikers Guide to the Galaxy",
        "quote": "Flying is learning how to throw yourself at the ground and miss."
    },
    {
        "author": "Richard Feynman",
        "quote": "The first principle is that you must not fool yourself -- and you are the easiest person to fool."
    },
    [...]
]

It is basically a list of quotes formatted in JSON. The data folder is where you can store additional data for Hugo. The data is accessible as a map in the .Site.Data variable.

We want to pull this information. Next, we will create a new partial, layouts/partials/quotes.html, and add this content:

{{ range (.Site.Data.quotes) | shuffle | first 2 }}
    <blockquote>{{ .quote}}
        <cite>{{ .author}}</cite>
    </blockquote>
{{ end }}

It retrieves our quotes: .Site.Data.quotes. Remember that the dot “.” refers to the current context, and this context includes site-wide variables, e.g., .Site.Data.quote is a map, an associative array, meaning it has key and value pairs. Our list of quotes, stored in the file /data/quotes.json, is accessible as a map in this variable.

It could loop over all the collection of quotes using range: range (.Site.Data.quotes), but we only want two random quotes. Then, we loop over these two quotes and access their two parameters: .quote, and .author, and render them in a blockquote.

You can use .Site.Data.quotes to pull all the quotes from the data file. However, we only want to retrieve two random quotes from the list, so we pipe the list to shuffle. Shuffle returns a random permutation of our quotes. Finally, we extract the first two quotes: first 2.

Finally, we will output this two quotes in layouts/_default/list.html:

{{ define "main" }}
{{- partial "quotes" . -}}
[...]

There are three options:

  1. Google Custon Search. However, it contains ads in the free tier.
  2. Using a simple HTML form
    <form role="search" method="get" action="https://www.google.com/search">
      <input type="search" placeholder="Search" value="" name="q" title="Search for:">
      <input type="hidden" name="sitesearch" value="justtothepoint.com">
      <button type="submit" value="Search"/>
    </form>
    
  3. Use Lunr.js (Download it and save it in the assets folder, assets/js/lunr.js). Credits Victoria Drake, blog, Add search to Hugo static sites with Lunr. First, we are going to create a search form. It is going to submit to /search. Create a new partial (layouts/partial/search-form.html). It will allow the user to input and submit a search query.
<form id="search"
    action='{{ with .GetPage "/search" }}{{.Permalink}}{{end}}' method="get" >
    <div class="col-12 justify-content-end d-flex">
    <label hidden for="search-input">Search site</label>
    <input type="text" id="search-input" name="query" placeholder="Search">
    <input type="submit" value="search"></div>
</form>

Create a new search page (content/search/_index.md) with its layout (layouts/search/list.html) that responds to a GET request and displays or render the search results:

{{ define "main" }}
<ol id="results">  
    <li>
        Please enter a keyword or keywords in the input above to search this site.
    </li>
</ol>
{{ end }}

Let’s build a search index (layouts/partials/search-index.html) for Lunr:

<script>
	window.store = {
		// Search all pages in our site:
		{{ range .Site.Pages }} 
		"{{ .Permalink }}": { 
			// Define your searchable fields using any .Page parameters
			"title": "{{ .Title  }}",
			"tags": [{{ range .Params.Tags }}"{{ . }}",{{ end }}],
			"description": "{{ .Description }}",
			"content": {{ .Content | plainify }}, 

plainify strips out any HTML tags and returns the plain text version of the provided string

			"url": "{{ .Permalink }}"
		},
		{{ end }}
	}
</script>
{{ $jsBundle2 := slice }}
<!--  Lunr Js  -->
{{ $jsBundle2 := $jsBundle2 | append (resources.Get "js/lunr.js") }}
<!--  Search Js -->
{{ $jsBundle2 := $jsBundle2 | append (resources.Get "js/search.js") }}
{{ $globalJS2 := $jsBundle2 | resources.Concat "js/global2.js" | minify | fingerprint }}
<script src="{{ $globalJS2.Permalink }}"></script>

Finally, let’s perform the search (assets/js/search.js). This is our javascript to retrieve the value entered by the user and generate a list of search results, if any.

//3. Display the results
function displayResults (results, store) {
        // Remember the line: <ol id="results"> (layouts/search/list.html) 
	const searchResults = document.getElementById('results')
	if (results.length) { // Are there any results?
	  let resultList = ''
	  // If there are some results, we iterate over them.
	  for (const n in results) {
		const item = store[results[n].ref]
		resultList += '<li><a href="' + item.url + '">' + item.title + '</a>. '  + item.description + '</li>'
	  }
	  searchResults.innerHTML = resultList
	} else {
	  searchResults.innerHTML = 'No results found.'
	}
  }
  
  //1. Get the query parameter(s). The search property of the Location interface is a search string that is a string contaning a '?' followed by the parameters of the URL.
  const params = new URLSearchParams(window.location.search)
  // URLSearchParams makes it very easy to parse out the parameters from the querystring. 
  const query = params.get('query')
  
  //2. Perform a search if there is a query
  if (query) {
	  // It keeps the search input value in the form after displaying the results
	  document.getElementById('search-input').setAttribute('value', query)
          // It intializes lunr with the fields it will be searching on.
          const idx = lunr(function () {
            this.ref('id')
            this.field('title', {
              boost: 15 // We are going to "boost" title, description, and content.
            })
            this.field('tags')
            this.field('description', {
              boost: 10
            })
            this.field('content', {
              boost: 5
            })
          
            // We add the data from the search index to Lunr
            for (const key in window.store) {
              this.add({
                id: key,
                title: window.store[key].title,
                tags: window.store[key].category,
                description: window.store[key].description,
                content: window.store[key].content
              })
	    }
	  })
  
          // Next, we get lunr to perform the search
          const results = idx.search(query)
          // Finally, we get lunr to display the search results
          displayResults(results, window.store)
    }

Bibliography


Hugo Documentation
Infinite Ink, #gohugo Portal
cloudcannon, Community resources, Tutorials.
The New Dynamic, articles.
Régis Philibert’s blog
Smashing Magazine, Hugo

Bitcoin donation

JustToThePoint Copyright © 2011 - 2022 PhD. Máximo Núñez Alarcón, Anawim. ALL RIGHTS RESERVED. Bilingual e-books, articles, and videos to help your child and your entire family succeed, develop a healthy lifestyle, and have a lot of fun.

This website uses cookies to improve your navigation experience.
By continuing, you are consenting to our use of cookies, in accordance with our Cookies Policy and Website Terms and Conditions of use.