Building a Static Site Generator with Pandoc and Bash

Here’s how to build your own JAMStack static site generator with nothing more than Pandoc and Bash. We’ll throw in some link validation with Muffet and automatic previewing with Browsersync.

πŸ’Ž Forget about all the other SSG engines out there. Why be constrained to their bad and inflexible designs when you can make your own in a weekend and maintain full control β€” forever. Ours will even have a built in link validator to make sure you never have a broken link on your site, ever (well at least none you don’t know about). None of the existing SSGs have anything like that.

But First …

You will need to install the following tools:

Step by Step

The best organization of content is flat. Each directory is a content node.

β”œβ”€β”€ index.html
β”œβ”€β”€ template.html
β”œβ”€β”€ contact
β”‚Β Β  β”œβ”€β”€ index.html
β”‚Β Β  └──
└── some-article
    β”œβ”€β”€ index.html
    β”œβ”€β”€ image.png
    β”œβ”€β”€ image2.png

The reason for this is to keep the URLs easy to use and remember:

For consistency we will always name the main Markdown file which allows others to access the Markdown file as reliably as the index.html. Thing of it as a web of Markdown files as well as HTML files.

This flatness allows your site to grow in an unlimited way allowing linking easily between the content nodes.

Note that the template.html is a special file that has the main shell for every single content node page with the $body$ template variable located where you want the Markdown rendered as HTML to be replaced.


build () {
  pandoc -s "$1" -o "$dir/index.html" --template=template.html


for path in  **/; do
  build $path

This is a working static site generator all by itself. Everything from here is just icing on the cake.

πŸ’Ž This might seem overly simplistic, but this is exactly the result of what VuePress does except VuePress is ridiculously slower at generating the output and also created unnecessary redundant copies of every content node page that it combines with every other page and keeps in memory. That is just supremely bad and unnecessary software design.


β€œCan’t I just use Muffet without making my own SSG?”

Yes, but chances are it will be much slower.

Having a locally rendered version of your site that muffet can check is so much faster than doing the same over the Internet that it is rather ridiculous not to setup your workflow to do your rendering and checking long before sending it up to the Internet.

Besides, you want to capture all those broken links before to push it into production.

Most of the SSG tools out there do have some method of local rendering, but many people use the rendering and building tools of the hosting provider (Netlify or GitLab or GitHub) instead forcing link checking into the step after pushing, which makes very little sense and is horribly inefficient.