Playing around with static site generators
Recently, I realized that my dual-language website was too much of a burden to maintain. So I looked around for options and settled with Hexo, a static site generator to rebuild my site from ground up. These are the lessons I’ve learned along the way.
Why do I need an application to generate a site?
For most of the time, I was stuck with hand-coding html pages. I mean, for an 8 page site this shouldn’t be a big problem. But recently, I’ve found that extending the structure is extremely bothersome, and wanted to spend my time with meaningful coding rather than checking whether links are good or not.
My site is pretty simple:
- one index page with the last 4 items of the changelog
- two pages of my proof-of-concept “products”
- mandatory “about” and “legal” pages
- and a changelog page with every change ever happened in the past.
Everything has to be in Hungarian (default), and also, in English. Every page / changelog entry has its English counterpart, and everything should be navigable. Nothing serious, huh?
Let’s start with a static site generator (SSG)!
A static site generator is a program, that takes your somewhat structured content — for example blog posts — processes it, and generates static HTML content with the appropriate design elements (headers, footers, style items, etc). The magic part is, that it has a very good, programmable templating system, which can be easily modified to generate the desired result — for example generating language-specific urls and cross-referencing pages.
Here’s the overall process:
With the SSG-s, it is advised to commit your content into a version control system — I prefer Git. In the cloud, you can use GitHub, BitBucket or GitLab (etc.), and lucky us, all of them has some kind of a build pipeline we can us later.
Either way, there’s a part where you create content, then you build the site, and finally, you deploy. What we aim for is after making the content, you shouldn’t do anything that can’t be automated.
Enter Hexo. Let’s dive in!
So getting the “hello world” out of Hexo was like a breeze. Hexo’s website gives a pretty good example of how to set up the system, and how to create your first posts.
I knew I had some special requirements with the design, so I needed to go into the details of templating. I chose to search for a tutorial on YouTube as well, and I absolutely recommend Mike Dane’s YouTube series of Hexo — static site generator playlist for getting in touch with Hexo.
These are the files / directories you should get acquainted:
- _config.yml — Hexo stores your configurations here, this is very important
- _posts/ directory— this is where all your “posts” will be generated by default
- themes/_insert_your_theme_name_/layout directory — if you want to tinker with how the site looks, you do it here
- scaffolds/ directory — this directory contains the templates for your posts and pages. This will only be used when you add new posts / pages.
Here are the steps to start building the site:
- Modify the template files to accommodate the Bootstrap-based template.
- Modify the template file for the index page and the archive page to fit the design and content. Also, include the links for the menu items to the desired pages.
- Modify the archive directory name to changelog, so the changelog/index.html will contain all the posts’ content.
- Create the pages for “about”, “legal” and product pages.
This is pretty straightforward. Are you following?
Complexity comes with cross-cutting features. What I would have liked to see is to have language-changing URL-s in the menu (for hu/en), and links to the respective pages’ other language counterpart.
I’ve searched Hexo’s site, GitHub issues, StackOverflow posts for getting the multi-language feature to work.
Lessons learned 1 — don’t confuse i18n support with multi-language
Hexo does not handle multi-language as you would expect. At least not out-of-the box. So when you try to finding a working example, please remember: i18n examples and features are for generating language-specific content in the layouts. It has nothing to do with the language of your posts / pages.
There are number of Hexo plugins that can help generating various contents, and there are at least 4 multi-language ones. Unfortunately, most of them are obsolete, abandoned or the features have been moved into the main branch. I was on a hurry, and didn’t want to go down the trial-and-error route, so I searched some more until I stumbled upon Ray Lee | 李宗叡’s “Build a multi-lingual blog with Hexo” page that introduced the Minos template, which had every concept I needed — except for the UI and the specifics.
After downloading the template, I made some heavy modifications to fit my web template into Minos. In the meantime, I was trying to figure out what did I do wrong before, not being able to get multi-language working.
Lessons learned 2 — templates in Hexo can (and usually do) alter the rendering process
The Minos template overrides the default behavior of the Hexo engine, and forcefully generates the index pages for the desired languages defined in _config.yml, and in the templates/minos/languages/ directory. Also, it takes care of filtering, so when you are on the English pages, you would get only the English language posts, on other language pages, you get the respective languages’ posts. So if you are searching for a solution, and you doesn’t understand why the internet example doesn’t work for you, it’s probably because the template does some tweaking with the generation process.
Hexo and Minos needs to understand where the language is coming from. In SSG-s, usually the directory path contains this element. The empty one is the default language, the others must be present — unless Hexo won’t know what the language code is, and uses the default.
There are some settings needed to be set up in order for this to work in the _config.yml file:
language: [hu, en]
new_post_name: :lang/:category/:title.md # File name of new posts
I had to play around a little with the settings — I felt like walking blindfolded in the woods, because the system wouldn’t react in a way I anticipated.
Lessons learned 3 — Hexo doesn’t read the changes in _config.yml when it’s serving pages
Usually, when you develop with hexo you use the
hexo servecommand to see online what your changes were. Everything works as expected, except for the config file. You have to restart the hexo server to make the changes effective.
Moving along, I created all the post with the
hexo new post postname --lang en and
hexo new post postname --lang hu
Hexo generates the file postname.md in the directory defined in the
permalink entry in the config.
Minos does it’s job wonderfully, so I can see my English posts when I’m in English mode, and my Hungarian ones when I click the Hungarian language setting. The archive pages contain only the posts with the respective languages, so everything works fine.
I tried this approach generating pages as well, but it won’t work.
Lessons learned 4 — Hexo’s language settings for creating content only apply to posts, and not pages.
So if you want your about/index.md page to have an English counterpart in en/about/index.md you have to manually alter the path where you want the file generated, like this:
hexo new page about --lang en --path en/about/indexDon’t forget the “index” part at the end, otherwise, Hexo will only generate en/pagename.md file. There’s a good explanation here.
Now we have everything in place, and the basics are working. Just some adjustments regarding the layouts and we are good to go.
Programming the templates
As I mentioned earlier I had some requirements that are not “out of the box” features of Minos. I had to modify the template, to fit my specifications. Let’s go through each of them, there are more lessons…
Lessons learned 5 — Don’t be alarmed when you have to modify the template for site-specific content.
Unlike other systems I worked with, Hexo/Minos does not make a clear separation between code/template/content. i18n lookup values are in the template/language/en.yml and hu.yml files. Menu items and urls are in the _config.yml in the template directory (yes, this is different than your _config.yml in your root folder). There are even language specific _config.yml files for language-specific menu definition.
This is very strange, coming from a background where I worked with Java frameworks, but you get used to it.
There’s also the way you try to make things work and test your modifications in the browser. Fortunately, with SSG-s, it’s just a browser reload away — there are plugins for detecting changes server side, and forcing the browser to update, but I didn’t try them.
Lessons learned 6 — If you develop the template, and your page is generated, it doesn’t mean you’re good. Keep your eyes on the terminal for error messages.
I experienced 3 types of behavior from
hexo serveduring template development: the HTML page is generated and it contains what you intended, a HTML page is generated, and it does not contain the changes you saved in your template, HTML page is not rendered until browser timeout.
The first one is self explanatory, the second one serves the last good render from a cache (probably the one you didn’t mess up), the last one is so severe, it froze the whole rendering process. So if something is not as it is expected, take a look at Hexo’s terminal.
When you modify index (summary) pages like categories, archive, or tags and mess something up, be prepared, that Hexo will throw an error during startup.
When programming the archive page, I had an assumption, that the posts don’t have a specific ordering. Or at least I shouldn’t trust them. So I tried to do the “sort by date” by hand, but I failed. One element always fell out of order and it drove me nuts.
Lessons learned 7 — don’t try to sort posts manually in the template generation process. Just don’t.
What I figured, is that the
page.postsvariable is managed by the rendering process, and either way you do a
sort()on it, the process rearranges the array. So instead, use the
index_generator/order_by: -dateproperty to specify the direction of the sorting in the
_config.ymlfile, and you can expect that
page.postshas the correct order.
At this point, everything looks fine. The multi-language site behaves as expected, links are working, pages show up, no disorder in the posts.
Until now, we were in developer mode, and we have to switch to “production”. Hexo has the
hexo generate command which, among many things, will generate files in the public/ directory:
- index.html — the main page for your website
- pages — all of the pages that are not posts
- archive — all of the posts, in year/month directory structure
- category summary page, and pages for each category
- tags summary and pages for each tags
- assets — everything from the template/source directory
Lesson learned 8 — When you place an HTML file in the source directory, it will be assumed, you want to use the template on that.
So if you want to have a file, that is not required to be templated by Hexo, you will need to add an exception in the _config.yml file: skip_render: '**/*.html'
So we have everything ready, but I realized that my output directory was too large. After some investigation, it turned out that every asset from every template in the templates directory was copied to the public folder.
Lessons learned 9 — Only use one template in the templates directory.
It’s tempting to play around with different templates, but you will use only one in the end, so you better clean up before generating the final content.
Now we have a working website in our public folder. You can copy/deploy it to the hosting service/site you want it to be accessed from.
The most important lesson
I’ve put up this article, because I think Hexo is a very good and versatile tool, but when I wanted to do something out of ordinary, it was very hard to get useful information.
After all I went through this learning process, would I recommend Hexo?
- If you have some font-end developer experience: yes, definitely. It’s super effective once you learn the tricks.
- If you don’t have any front-end developer experience: no. Just no. This is not a portal engine.
We have to admit, that SSGs are tools for developers who don’t mind to get their hand a bit dirty to build their own sites very effectively. But it’s not for the average Joe.