Getting Started with Wintersmith: A Node.js-based Static Site Generator

I’ve spoken and written on the topic of static site generators a lot lately. Generally speaking, I tend to recommend going with Jekyll as the most stable and feature-rich option for building static sites available today. However, the fact that Jekyll is Ruby-based can be a deal-breaker for some developers, especially those not familiar with Ruby. The single most frequent question I get is: “Is there a good option based upon JavaScript and available via npm?”

In this article, first of a two-part series, we’ll cover one such option, Wintersmith. Wintersmith is a robust static site generator with one biggest impediment being (in my opinion): a lack of detailed documentation. Hopefully this article will help anyone interested in a JavaScript-based option available via npm to get started building static sites with Wintersmith.

The Sample Site

For this article, we’ll use the sample site I built and that you can find here. The sample is an Adventure Time! fan site that looks like the image below.

--ADVERTISEMENT--

The goal of the project was to rebuild this site using a number of static site generators to compare them. While the site is intentionally simple, it contains a number of key features that we’ll look at in Wintersmith:

Custom global metadata – the ability to set custom metadata global to the site that can be accessed and used in templates;

Custom post metadata – the ability to set custom metadata on a per post basis that can be accessed when listing the post or in the display of the post itself;

Data sets – the ability to add content that is not a post or page, but rather a custom data type (in the case of this sample that is the character data).

All of the character data, content and images used in the sample are from the Adventure Time! wiki. The design was based upon a free template from HTML5UP.

How to Set up Wintersmith

One of the benefits of Wintersmith being based on Node.js and npm is that the installation process is super simple. You have to execute the command below to install it (note: the sudo isn’t necessary on Windows):

$ sudo npm install -g wintersmith

That’s it – you’re good to go! Now let’s create a new project.

Creating a Site

To create a new site using Wintersmith, enter the following command:

$ wintersmith new [project name]

For the example site, we’ll give the project a name of “wintersmithsite”. So the command to execute is:

$ wintersmith new wintersmithsite

It will generate a folder with the given project name that includes a bunch of files that we can modify to start building our website.

If we take a look at the generated files, we’ll see that Wintersmith places the configuration, the templates, and the plugins at the root level while the site files are placed within a folder named “contents”.

Testing the Site

To run the project on a local server, change directory and start a preview:

$ cd wintersmithsite
$ wintersmith preview

By default, the local server runs on port 8080, so we can open the site by browsing to http://localhost:8080. We can specify a different port using the -p option. Besides, by default, the server is verbose and will output detailed error messages and loaded resources to the console. There are a number of other options to the server which we can learn by entering the command:

$ wintersmith preview -help

The options can also be set within the site configuration file that is named config.json, but for now, the defaults should work fine.

Templating Basics

Wintersmith uses Jade as its templating language by default. This tutorial will use it, but Wintersmith has a good number of plugins available if you prefer a different templating language.

Templates go in the “templates” folder in the root directory of the site. Jade is a very terse templating language – there are no brackets, no closing tags and indentation matters. Let’s look at some of the basics of how you can create templates using Jade.

Output Data

Jade provides multiple ways of outputting data from variables. The most common when building a site template is to set a tag equal to the value of a variable. For instance, the following example from templates/article.jade will place the title of an article within a opening and closing <h2> tags.

h2= page.title

By default the contents of the variable are escaped before outputting. This means that if it contains HTML, the tags will not be rendered in the output but, rather, displayed as plain text. When we need them unescaped, we have to add an exclamation point, as in this example from templates/article.jade:

section.content!= typogr(page.html).typogrify()

We can do the same with attributes. The following example from templates/partials/homepagemiddle.jade creates an <a> tag with the href attribute equal to the article’s URL.

a(href= article.url, class="image featured")

If you’re curious what variables are made available on a page object by default, the documentation lists them. I should note that the article variable above is not a default but is the result of a loop, which we’ll discuss later on.

Another way to output variables using Jade is to use #{ variableName }. When we do this, the content of the variable is escaped. There are no examples of this method within our sample.

If you want to output the contents of a variable unescaped, the syntax to use is !{ variableName }. For example, when we are outputting the content of the body of a post, we’ll want any tags within it to be rendered. One example is taken from templates/partials/homepagemiddle.jade:

| !{ typogr(article.intro).typogrify() }

The pipe preceding the previous line of code means that the content will be showed as plain text.

npm Modules

You may be wondering what the typogrify() calls are all about. Well, one of the benefits of Wintersmith is that it supports the use of npm modules. The generated site includes three: typogr (which is what you see used above); Moment.js (to learn more about moment, you can read the article Managing Dates and Times Using Moment.js); and Underscore.

Let’s look at using Moment.js for formatting a date within a template, as in this example from templates/partials/homepagemiddle.jade:

p= "Posted " + moment.utc(article.date).format('MMM DD, YYYY')

Moment.js offers a lot more functionality than just formatting, and all of that is available from within our templates. But, we aren’t limited to only Moment.js, as we can just add any npm module to the require section of the config.json for our site, npm install it and use the module in our templates.

Includes

We’ll want to split up templates in order to make them more maintainable and reusable. We can do this using includes. This code from templates/index.jade includes the templates/partials/header.jade file (notice you do not need the .jade file extension):

include ./partials/header

Jade also supports inheritance, which can be used for creating similar, separate, and reusable blocks of template code. If you want more details on inheritance, check the documentation.

Conditionals

There are cases where you might want to display different aspects of a template based upon certain conditions. This can be done using conditionals in Jade. Jade supports if, else if, else and unless, which is effectively a negated version of if.

This example from templates/partials/header.jade only displays the banner if we are not on a page (every post in our site is a page, so this means only display it on the home page, index.html):

if !page
section(id="banner")
header
h2 Explore the Land of Ooo...
p ...and its many kingdoms!

Looping

Looping is something we’ll do a lot of in our templates, whether we are looping through posts or data. For such needs, Jade supports both each and while loops.

The following example from templates/partials/homepagemiddle.jade outputs all of our character data using an each loop. In the middle of the home page, we display each character with its image, name, and description. The each loop iterates over every object in the array and assigns it to the variable character where we can access its properties.

Unfortunately, there is no support for adding a limit or an offset to a loop. Instead, we can do this by combining variables and conditionals. In the following example, we are only showing the first two posts (similar to limit). Keep in mind that the lines setting variables (i and articles) have a preceding - to indicate that they will run on the server during compile time. This means that there is no corresponding code generated in the output of the template.

You will notice that we use env.helpers.getArticles(contents); to get an array of articles in the contents/articles folder. This isn’t well documented from what I could tell, but this method comes from the paginator plugin, which can be configured in config.json.

The next example and last example of this article replicates using both an offset and a limit to show the next five articles after the first two:

Conclusion

In this article I’ve introduced you to Wintersmith, which is a solid option if you’re looking for a Node.js-based static site generator. I covered how to install and get started with Wintersmith and also discussed some features of Jade, its default templating system. In the second installment, I’ll teach you how to create posts using the Markdown format, how to set custom Metadata, and also how to generate and deploy your static website.

As you’ve seen, one of the interesting aspects of Wintersmith is its ability to leverage npm modules. This offers developers a lot of choices when customizing their site or add additional functionality that they may need.

Brian Rinaldi is the Developer Content Manager on the Developer Relations team at Telerik. A large part of Brian’s work focuses on ensuring top notch content for the web development community on the
Telerik Developer Network. Brian has been a web developer for over fifteen years. Recently, Brian founded a popular developer site called Flippin’ Awesome (now Modern Web) and serves as co-editor of
Mobile Web Weekly. Brian writes regularly and also tweets the best articles, tutorials and new projects he finds @remotesynth.

Replies

Hey Brian. I was wondering how many other Node/JavaScript based static site generators you've tried. I know I saw a presentation you did on a few different SSG's not too long ago and the one JS one you presented was something you hated (because you hate Jade... which makes this article a bit ironic). Anyway since then, I found https://www.staticgen.com/ which lists tons of these SSG's and I grabbed the JS one with the most stars (Hexo) and so far I like it. I want to make sure I get the real deal, though, so if you've tried Hexo, it'd be cool if you could give a comparison. Otherwise I could be like you and try way too many of these things myself.

There are TONS of these - https://staticsitegenerators.net/ says there are almost 400. It's nuts. Anyway, I have not messed with that many of the npm options. Spent a lot of time with Harp as well and worked a bit with Metalsmith. I have not tried Hexo but it looks like it has potential. At least it is documented (a total copy of Jekyll docs but that's ok, as the Jekyll docs aren't bad). I like the command for new posts and the assets folder - Jekyll doesn't even have these. I've never used Swig (jeez, another template language!) but it looks similar to Liquid. I actually have a presentation coming at JekyllConf (online event) comparing Jekyll to the competition and will have to add this to the list.

Fwiw, I still hate Jade. I tried not to harp on this topic in the article - didn't want to be too negative.

Yea, for the most part, I like Hexo's documentation, but it is sometimes lacking some information or uses a pretty terse answer that doesn't really help much, especially for configuration. It also seems pretty flexible regarding the templating languages you use, which can actually be annoying since none of the themes I downloaded to look at used the recommended Swig, though that may have something to do with the fact that they recently switched to it in version 3.

I'm not really sure of the point of Swig... it's so similar to so many other templating languages, but at least it isn't Jade . I wish people would stop making alternatives and just help out on projects that already exist unless you're actually working to create something completely different. Same goes for sooo many open source projects.

I personally like the idea of Jade's syntax, but I can't bring myself to learning a completely different HTML syntax. Most templating languages allows you to only need to learn new code for the dynamic markup, but let you use plain HTML everywhere else, which is a huge plus. Once you get used to Jade, it can help speed up development, but it's just another language you need to learn because you still need to know HTML.

Btw, you spurred me to write a version of that demo using Hexo. About to post it to my GitHub repo. The documentation wasn't as great as it looked. It lacked so many important details that cost me hours of effort and a lot of frustration. In the end, for instance, one thing I just lived with the workaround because I couldn't get it to work the way I'd expected.

Net result, I still like Jekyll best. If you must use Node/npm, then Wintersmith still wins.