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

File upload and Progress events with HTML5 XmlHttpRequest Level 2

The XmlHttpRequest Level 2 specification adds several enhancements to the XmlHttpRequest object. Last week I had blogged about cross-origin-requests and how it is different from Flash\Silverlight's approach .  With Level 2 specification one can upload the file to the server by passing the file object to the send method. In this post I'll try to explore uploading file using XmlHttpRequest 2 in conjunction with the progress events. I'll also provide a description on the new HTML5 tag -  progress which can be updated while the file is being uploaded to the server. And of course, some ColdFusion code that will show how the file is accepted and stored on the server directory.

Server sent events with HTML5 and ColdFusion

There are several ways to interact with the server apart from the traditional request\response and refresh all protocol. They are polling, long polling, Ajax and Websockets ( pusherapp ). Of all these Ajax and Websockets have been very popular. There is another way to interact with the server such that the server can send notifications to the client using Server Sent Events (SSE) . SSE is a part of HTML5 spec:  http://dev.w3.org/html5/eventsource/

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.