Blog / Craft

How to create a changelog similar to Headway with a static site generator

Having a changelog is a perfect way to let your current and future customers know about everything happening with your product. An invaluable tool for marketing.

Headway is a tool to keep your customers in the loop about your product. It provides an editor to create a page with changelogs and additionally a JavaScript widget. Alternatives are Beamer and AnnounceKit.

Changelogs are public-facing updates that highlight changes and improvements to your software product.

It looks something like this:

Preview of Headway's changelog

If you have seen Spinal’s changelog you might see the resemblance. In fact behind the scenes, at Spinal, the same kind of markup tags are used so [new] will turn in those colourful badges. [bugfix] and [improvement] are also supported.

Preview of Spinal's changelog

How to create such changelog with a Static Site Generator? #

Let’s check out how this is done in Spinal. Every changelog in Spinal is one markdown file. The only Frontmatter it holds is a date field.

Everything else is stored in the “body” of the file.

Preview of a changelog item in Spinal's editor

The beauty of this is that this very understandable for non-tech writers. What’s more is that in Spinal these badges are added as “snippets” so they are really easily inserted.

Every update is preceded with one of those badges in brackets (`[]`). Upon building the site those get transformed into html. How to do that depends on your Static Site Generator of choice.

But for most it boils down to the same concept a regex-based replacement. Something along the lines of:

{{ content | replace: /(\[(new|improvement|bugfix)\])/i, '<span class="badge badge--\2">\1</span>' }}

This is based on a liquid filter in Bridgetown. For JavaScript-based static site generators a function like the following might work:

function generateBadges(content) {
const badgeRegex = /\[([\w\s]+)\]/g;

  const badges = {
    new: 'New feature',
    improvement: 'Improvement',
    bugfix: 'Bugfix'


  return content.replace(badgeRegex, (match, type) => {
    const text = badges[type.toLowerCase()] || '';

    return text ? <span class="badge badge--${type.toLowerCase()}">${text}</span> : match;



module.exports = { generateBadges };

Sorry, this isn’t a Cmd+C, Cmd+V kind of situation. Check your SSG of choice how exactly to do this.

We open-sourced the layout of Spinal’s changelog. You can use this template, built with HTML + Tailwind CSS as a starting point for your own SSG-powered changelog.

Written by July Forand


Get all Spinal content in your inbox

Every first Thursday of the month, we'll send the latest about Spinal in your inbox. From product updates, articles and a little peek behind the scene of building a SaaS in 2024.

Get all the latest every first Thursday of the month. No spam. Unsubscribe at any time.