Migrating to Zurb Panini 2.0

  Filed in: Panini,static sites

2.0 Paninis. Or is it just 2.0 Panini? Paninodes? Thanks to Flickr user Stephanie Vacher for the image [CC License]

Not yet stable yet, but has some compelling features.

Why upgrade to 2.0?

I've been using Zurb's Panini static site generating template to power this site. On the whole I've generally found it quite good, but there's been a couple rough edges that continued to paper cut when creating content (even though I realise Panini wasn't made with blogging in mind).

One thorny limitation is there's no seamless way to generate a blog index or RSS feed. So in versions 1.X, I had to create and manually maintain a YML file with each blog entry.

Not the end of the world, but it's a copy paste job that would occasionally lead to mistakes and was tedious; an excerpt of my old manually rolled data/blog.yml file:

- title: "Actioning web strategy & IA: The Content-Action Method for Web Systems"
layout: layouts/post.njk
teaser: "A method to distill content and user, organisational goals into an interconnected, holistic practice."
image: "/blog/drill-bits.jpg"
date: 2018-01-22
url: /posts/20180122-content-action-model.html

And beyond being tedious to create and maintain (copy-pasting each page's meta data), the method only gave me access to what's in the YML file — so that meant my RSS feed couldn't have the full blog posts:

\{{#each blog}}
\{{/each blog}}

But, lo! In version 2.0, a new \ variable.

\{{#each-reverse pages}}
\{{#ifequal this.[2].active_path "blog"}}
{{#compileBlogPostForRSS this}}\{{{ ../this }}}\{{/compileBlogPostForRSS}}

The magic happens in my custom {{#compileBlogPostForRSS}} helper, where I pass in the full body of each post and render it as a Handlebars template. Here's a link to that helper.

The approach is slightly messy as I must reach into the second [2] spot of each page array, but it does the job. (n.b. This is meant to get better by changing from an arraay to named object.)

I now use the same approach to build this site's Blog index.

Upgrading from 1.x to 2.0

It's not so bad.

Remember Panini 2.0 isn't finalised yet, but as Zurb points out:

It's pretty stable, and if you want to give it a try, we'd love some feedback or bug reports on anything you run into.

Panini 1.X defaulted to use Gulp, and it was what worked best for me. So this guide is from gulp to gulp.

  1. Change your packages.json to use 2.0: "panini": "^2.0.0-alpha.2" (Previously I was using Panini 1.5.1)
  2. In your gulpfile.js drop var panini = require('panini'); and add const panini = require('panini/gulp');
  3. Panini now has default expectations about the naming of layouts, partials, etc. So my Gulp Panini task is now just (diff):
gulp.task('panini', () => {
  return panini('src', {
    // builtins: false,
  1. If you're using browserlab, panini.refresh(); is a bit harder to use and is going away. Instead my gulp.watch task now invokes panini.create();. That may not be the most efficient way, but it works.
  2. #ifequal is no longer a built in helper, instead it's eq. But I didn't want to find-replace, so I just added the ifequal helper back.

Misc changes

I had to do a few other things that were unique to my setup.

  1. I was generating my critical path CSS as a partial called critical.min.css.html (🚨hack alert). It doesn't seem naming with dots are allowed in partials anymore, so I've called the file criticalmincss.html
  1. Previously I didn't have my layouts,partials,data,pages under a directory. I moved them to ./src.

Here's the commit from my upgrade to 2.0.

Other neat things

I've not yet used these yet, but they look like they'll really extend how long I can use Panini:

Panini still feels like a bit of unusual static templating engine. It's less feature complete than some, yet it also feels more light and tool-like than others. But I've enjoyed using it and it's been helpful, so thanks Zurb and contributors!

Check out Panini 2.0 here

N.b. I don't think there's an existing Panini sample/quick-start blog project, if there's interest I could convert this site into a "Panini blog boilerplate".