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

Using data with Hugo. Search with Hugo.

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. Lunr.js

    Lunr.js

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
Awesome Hugo is a curated list of awesome things related to Hugo.
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 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.