Mastering metalsmith: best practices for static sites

In our previous blog post, we discussed how we built the new evocode.com using metalsmith, a static site builder. We learned quite a few things along the way and want to share some of the best practices we picked up. We open sourced our metalsmith base starter kit on github that demonstrates some of these.

Metalsmith plugin priority matters

We initially setup metalsmith plugins by adding them to metalsmith like you add dependencies to your package.json. This quickly led to unexpected results and a lot of broken behavior. You have to take into account what each plugin does, and then prioritize them. If the plugin adds metadata that another plugin should use, it naturally needs to come before you use it.

For example, you may have a pagination plugin that needs to run before you compile your files with a template engine, as that template engine needs to know about the pagination. Another example is that you need to exclude any drafts and convert your markdown files to html, before you can minify that html. I did not find this documented anywhere so it took some juggling of plugins before I assembled a usable order.

Metalsmith(__dirname)
  .use(drafts())
  .use(markdown())
  .use(permalinks())
  .use(collections())
  .use(pagination())
  .use(layouts())
  .use(assets())
  .use(minify())

Compile your assets outside of metalsmith

Metalsmith has a number of plugins that wrap other libraries like autoprefixer, gulp, webpack, sass, postcss, etc. These are simple plugins that wrap popular node libraries that add a layer of abstraction that need to be updated. Take metalsmith-sass for example, it tries to detect whether the files that metalsmith has parsed are sass files and then tosses them into node-sass.

This abstraction now needs to be updated alongside node-sass which often causes unnecessary delays and couples your asset building to metalsmith, instead of node itself. You also speed up the build process as you can now live reload your assets using something like webpack without rebuilding the whole site. The only thing metalsmith should be responsible for is parsing and compiling your pages. All other css, javascript, images, etc should be handle by a separate task runner like gulp or grunt.

When to use the CLI

Metalsmith comes bundled with a command line interface that reads from a metalsmith.json file. We initially started with the CLI but found it limited in that you can’t dynamically generate the plugins, options, or metadata as it uses a static json file. We did enjoy the syntax of the metalsmith.json file, so we mirrored that in our site.js file to provide a similar experience, but remain flexible to provide dynamic data.

When to use Make

In all of the metalsmith examples, they provide a simple Makefile as means to build your site. This adds another dependency, although minimal, when node / npm already has a similar tool at our disposal. We used npm scripts to kick off our build process rather then add another layer: npm run build.

{
  "scripts": {
    "start": "./node_modules/.bin/gulp server",
    "debug": "DEBUG=metalsmith* ./node_modules/.bin/gulp",
    "build": "./node_modules/.bin/gulp --production && ./build.sh"
  }
}

Debugging Metalsmith

DEBUG=metalsmith* ./node_modules/.bin/gulp

Metalsmith uses the popular debug library, which means we can easily add an environment variable before executing metalsmith to find out what is going on. One drawback we found is that not all metalsmith plugins follow this same convention, or have useful debug output, which makes this a bit challenging. However, enabling debugging is critical to find out what is happening behind the scenes.

Metadata formatting and escaping

---
title: 'Mastering metalsmith: best practices for static sites'
url: 'mastering-metalsmith-best-practices-for-static-sites'
---

Metalsmith uses YAML for setting metadata in individual files and we often ran into issues by forgetting to escape our variables. When using : for example, you must wrap the values in quotes. Unfortunately metalsmith does not error on invalid YAML, even when debugging, and instead produces unexpected results, forcing you to track down the issue.

If you ever find your content not showing up or named incorrectly, it is likely because the metadata was not parsed correctly. We are trying to track down this issue upstream and submit a pull request to help improve this process in the future.

Deployment process

Once you use metalsmith to compile your site locally, you need to push it to a server. We have an ansible based GIT deployment process right now. As our site is simple, it doesn’t make sense to setup a continuous integration server to build the site and deploy the artifacts. We also didn’t like the thought of bundling the compiled assets in the same repository.

We setup a build process that updates a separate GIT repository for the compiled site, which integrates into our existing ansible deployment process. This keeps our repostiory clean, conflict free, and our deployment options open to change. The build process currently checks out the remote repository, runs the build, and then stops for manual review. The review step allows us to vet the compiled output for errors before pushing anything to production. After a simple git commit and ansible command, it’s live within seconds.

Metalsmith drawbacks

Lack of documentation. The biggest drawback of metalsmith is the lack of any sort of documentation. You definitely need to be well versed in JavaScript and NodeJS to be able to work through any issues when you ultimately get stuck. The plugins are a hit or miss in how well they are documented.

Difficult for complex sites. The beauty of metalsmith is that everything is a plugin, however this makes it challenging to build more complex functionality. If you need to implement tagging, categories, pagination, content via APIs, redirects, SEO / social, etc you are left trying to figure out what plugins to stack together or building your own. I often found that some plugins were outdated, broken, and were not compatible with each other.

Metalsmith starter kit

We open sourced our own metalsmith base starter kit to help with some of these challenges in buliding with metalsmith. Some features include: gulp, development server, bootstrap 4, babel, ES6, jQuery, MIT license, static site, blog, robots, anbd sitemaps.

We will be improving the documentation of our starter kit going forward. Feel free to fork it on Github and test it out for your next static site!

Get the starter kit