Skip to main content

On Static Site Generator - Hugo

Over the last few weeks, I've been looking into a Static Site Generator - Hugo. A Static Site Generator is useful if you're building an application which does not require dynamic data to be served. A blog can be considered as a service which serves static content. Instead of storing the content in a database field, the content is stored in a file (HTML file). Thus when a page is requested the content is served immediately instead of it being generated on demand; resulting in accelerated response times and thus better user experience.

Hugo helps in generating a static site. It has got several features such as defining layouts for various types of content, displaying a list of related content, tagging content (taxonomies), using themes and also generating content from data in JSON, YAML or TOML files.

Hugo provides a Command Line Interface to generate content. After installing Hugo (brew install hugo), create a new site using the command

hugo new site hugo_bootstrap_example 

Hugo creates a directory structure as detailed below:

--archtypes
--content
--data
--layouts
--static
--themes


All content should be present in the 'content' directory. If you are considering to build a blog, then this directory would contain all the blog posts. To create a new post use the command:

hugo new posts/blog-post-1.md

This will create a markdown file blog-post-1.md under 'content/posts' directory. The directory 'posts' is created if it doesn't exist. When you open the file, you'll notice that it contains some meta-data in it, it's called front matter. By default the front matter contains 'date' and 'title',  here date being the created date and title being the file name:

+++
date = "2016-12-04T16:13:35+05:30"
title = "blog post 1"

+++


The front matter is enclosed in +++, any text that follows after the last '+++' will constitute the content or the body of the post. Hugo organises the output in the same way as it is declared i.e. to view the post you can access the URL <site>/post/blog-post-1. This can be changed by updating the front matter to include section, path and slug fields (more on this in the next post).

On Layouts:

Once you have defined the type of content that you want to render, you need to define how this content will be rendered. For example, if you have created two different types of content - 'posts' and 'pages' then you need to define views for 'posts' and 'pages' in the 'layouts' directory. This is required because Hugo does not know how to render the content. It took me some time to understand how Hugo does this; it looks up for templates defined in the layout directory and generates static files based on the number of files present in the 'content' directory. This also means that if you have defined content but the corresponding layout is missing, then no static file is generated for that type of content.

Note: If you would like to use the same layout for all types of content, then create a directory '_default' under layouts and then define the view templates under it.

Let's create a template for 'posts':

--layouts
  --posts
    --single.html


Here single.html is a template file, that defines how the post would look like when the user accesses the URL <site>/posts/blog-post-1. In addition to displaying the title and the content of the post, you can display the date on which the post was published:

<!DOCTYPE html>
<html>
<head>
  <title>{{ .Title }}</title>
</head>
<body>
  <h1>
    <a href="{{ .Permalink }}">
      {{ .Title }}
    </a>
  </h1>
  {{ .Content }}
  <h6>Published on: {{ .Date.Format "Mon 2, Jan 2016" }}</h6>
</body>
</html>


Hugo uses GO templates to generate the content. In the above example, 'Title', 'Permalink', 'Content' and 'Date' are template variables, which are used to generate static files based on the front matter and the content defined in the file 'blog-post-1.md'.

In addition to defining single post view, you can also define a list view (li.html) - a view to display a list of content posts instead of one single post and a summary view (summary.html) - to view only a part of the content.

Rendering the site using Hugo server:

Before we get to rendering the content on the browser, Hugo requires some configuration to display the content. This configuration contains site information such as the 'baseUrl', 'title', 'languageCode' and 'theme'. When you generate the site, you'll have the file 'config.toml' created, which contains the above configuration fields. All that is required is to provide the values for the above fields. Here's the example 'config.toml' file content:

baseurl = "localhost:1313"
title = "My New Hugo Site"
languageCode = "en-us"
theme = "ex_bootstrap_theme"

Hugo comes with a built-in web server and you can start it using the command:

hugo server

It starts the server at port 1313 and you can try the URL localhost:1313. This command will generate the static content and will watch for the file changes in directories generated by the hugo new site command.

On Themes:

A Theme, like Layouts, is used to define templates. There's not much difference between a theme and a layout, except that a theme should be created if you want to reuse it for a different site. This will enable you to encapsulate the look and feel of the site along with behaviour (JavaScript) and share it with others who can then follow the same convention to build the site.

I'm inclined towards using a theme over layouts only for the sole reason that it can be shared with other users.

Hugo does not ship with a default theme, however, a theme is required to run the server. You can download one of the many themes available at http://themes.gohugo.io/ or create your own theme using the command:

hugo new theme <theme_name>

Summary:

I really liked Hugo and I'm getting used to GO templates. There are tonnes of options available to customise the site and I'm still exploring these options. The biggest selling point is it's performance when generating static content. I haven't tested the site containing several pages, but it takes say about 12ms to generate a very small site with 4 pages in it, which is extremely fast.

Hugo does not dictate how your site should be styled, however, it provides a directory 'static' where all your CSS, Images and JavaScript files should be placed.

Comments

Popular posts from this blog

Adding beforeRender and afterRender functions to a Backbone View

I was working on a Backbone application that updated the DOM when a response was received from the server. In a Backbone View, the initialize method would perform some operations and then call the render method to update the view. This worked fine, however there was scenario where in I wanted to perform some tasks before and after rendering the view. This can be considered as firing an event before and after the function had completed its execution. I found a very simple way to do this with Underscore's wrap method.

De-obfuscating javascript code in Chrome Developer Tools

I had blogged about JavaScript debugging with Chrome Developer Tools  some time back, wherein I have explained how these developer tools can help in debugging javascript code. Today Google Chrome 12 was released and my Chrome browser was updated to this version. As with every release, there have been some improvements made on performance, usability etc,. One feature that stood out for me is the ability to De-obfuscate the javascript code. What is Minification? Minification is the process of removing unnecessary characters such as white spaces, comments, new lines from the source code. These otherwise would be added to make the code more readable. Minifying the source code helps in reducing the file size and thereby reducing the time taken to download the file. This is the reason why most of the popular javascript libraries such as jQuery are minified. A minified jQuery file is of 31 KB in size where as an uncompressed one is about 229 KB. Unfortunately, debugging minified javascript f

On GraphQL and building an application using React Apollo

When I visualize building an application, I would think of using React and Redux on the front-end which talks to a set of RESTful services built with Node and Hapi (or Express). However, over a period of time, I've realized that this approach does not scale well when you add new features to the front-end. For example, consider a page that displays user information along with courses that a user has enrolled in. At a later point, you decide to add a section that displays popular book titles that one can view and purchase. If every entity is considered as a microservice then to get data from three different microservices would require three http  requests to be sent by the front-end app. The performance of the app would degrade with the increase in the number of http requests. I read about GraphQL and knew that it is an ideal way of building an app and I need not look forward to anything else. The GraphQL layer can be viewed as a facade which sits on top of your RESTful services o