JS Templating: Transitioning from swig to nunjucks

In JavaScript land the pace of development moves very fast and there are literally hundreds of competing libraries to accomplish almost anything you can imagine. JavaScript templating can be tricky because there are so many different options it’s difficult to know which one to choose. While building out developers.optimizely.com I chose to use swig because we had used it in a few other projects already and it seemed to have a good community built up around it. However, swig is no longer going to be maintained by its creator (although now it looks like members of the community are stepping up to try and maintain it). This was enough of an impetus to get me looking around at alternative templating solutions.

One of the alternative JavaScript templating solutions that really stood out to me is called “nunjucks”. Nunjucks is maintained by Mozilla and used in their products so it is likely to stay maintained for the foreseeable future. Additionally, swig and nunjucks both function very similarly and both are derived from jinja2 templating syntax. In this article I’m going to be highlighting some of the key differences that I’ve noticed while making the switch and provide tips for anyone looking to make a similar transition.

1. Python Style Logical Operators and Comparison Operators

In the example below, notice the use of the literal word `or` instead of `||` which JS uses to signify or. Additionally, the ‘triple equals’ strict comparison operators aren’t needed either., so `===` becomes `==` and `!==` becomes `!=`.

<!-- swig --> 
{% if path === 'index' || path === 'home' %}

{% endif %}
<!-- nunjucks -->
{% if path == 'index' or path == 'home' %}

{% endif %}

Changes:

  • Change `||` to `or`
  • Change `&&` to `and`
  • Change `===` to `==`
  • Change `!==` to `!=`

2. The include tag in nunjucks doesn’t accept a context object

In swig you can pass a context object in as a part of the include tag, however in nunjucks the included template simply inherits the context of the template that contains the include tag.

{% set data = {
foo: {
title: "First"
},
bar: {
title: "Second"
},
baz: {
title: "Third"
}
} %}
<!-- swig -->
{% for key, object in data | sort %}
{% include 'partials/_item.html' object %}
{% endfor %}
<!-- In the _item.html file you can access the `title` property directly -->
<!-- nunjucks -->
{% for key, object in data | dictsort %}
{% include 'partials/_item.html' %}
{% endfor %}
<!-- In the _item.html file you would access the `title` property with `object.title` -->

Changes:

  • Refactor `include` tags to not pass a context object

3. For loops in nunjucks only accept one parameter when looping over arrays

In swig when iterating over an array with a `for` tag, you are able to pass two parameters in. The first parameter will be the index of the array, and the second parameter will be the value of that item in the array. However, in nunjucks looping over an array only accepts one parameter and the second one is ignored. The best way to address this is to switch to using `loop.index0` (that’s `index` with a zero on the end) which returns “the current iteration of the loop (0 indexed)” and works the same in both swig and nunjucks.

<!-- swig --> 
{% for index, item in ['a', 'b', 'c'] %}
[ {{ index }} - {{ item }} - {{ loop.index0 }} ]
{% endfor %}
<!-- result: [ 0 - a - 0 ] [ 1 - b - 1 ] [ 2 - c - 2 ] -->
<!-- nunjucks --> 
{% for index, item in ['a', 'b', 'c'] %}
[ {{ index }} - {{ item }} - {{ loop.index0 }} ]
{% endfor %}
<!-- result: [ a - - 0 ] [ b - - 1 ] [ c - - 2 ] -->

Changes:

  • Refactor `for` tags that iterate over arrays to only pass one parameter in

4. Switch to using the `dictsort` filter to make for loops in nunjucks iterate over object keys in alphabetical order

In swig, using the `sort` filter would work with both arrays as well as object keys, but in nunjucks the `sort` filter is still used for arrays, but `dictsort` needs to be used when iterating over object keys in order.

<!-- swig -->
{% for key, object in data | sort %}
{{ key }} - {{ object.title }}
{% endfor %}
<!-- nunjucks -->
{% for key, object in data | dictsort %}
{{ key }} - {{ object.title }}
{% endfor %}

Changes:

  • When iterating over objects switch to using the `dictsort` filter to iterate over object keys in alphabetical order.

5. Markdown tag name is different

This would probably only apply to people who are currently using swig-marked to provide markdown support in their swig templates, but it’s worthwhile to note. There is a similar package for nunjucks, called nunjucks-markdown. However, the main difference between the two seems to be that nunjucks-markdown adds a `markdown` tag instead of the tag being called `marked`.

One other change I made was to switch to using markdown for highlighted code samples as well, instead of using swig-highlight.

Changes:

  • Switch to using nunjucks-markdown
  • Replace `{% marked %}` and `{% endmarked %}` with `{% markdown %}` and `{% endmarkdown %}` respectively

Conclusion

Although it’s unfortunate that swig is no longer going to be maintained by its creator, it’s very fortunate that the open source community seems to be taking ownership of swig to help keep it maintained. Additionally, it’s great that there are many other awesome alternatives such as nunjucks. If you haven’t heard of nunjucks I highly recommend you go and check it out. Hopefully you found this article useful if you are considering making a similar change.