Update - January 2022

As of Hugo version 0.91, this method no longer works. :(

Hugo introduced a new security policy, and the only argument that can be passed to Pandoc seems to be --mathjax. Which of course, is the whole point of this article - to pass other options!

I am staying on version 0.90 for now, but will likely look for a long term solution down the road.

Some Context

I use a tool called Hugo to build this site. Hugo takes my Markdown-formatted blog posts, and converts them into HTML, a web-friendly format.

This conversion process works great. But for some blog posts, I need more control over how my content is rendered. For these cases, Hugo supports alternative rendering methods, including Pandoc.

The Problem…

The limitation, however, is that I can’t tell Hugo how to call Pandoc. Hugo will simply call pandoc --mathjax. If I want to supply different options to Pandoc, then I’m out of luck.

With the official Hugo software, at least.

…and the Workaround

If you don’t mind a minor workaround, this GitHub comment shows us how to customize the Pandoc command Hugo calls. We essentially create a light wrapper around Hugo, and it only takes a few steps.

In this blog post, I’ll be walking you through his solution, and adding my own commentary on the side.

TL;DR: Read that GitHub comment, and if you are on Windows, swap out the Bash script for this.

Customizing Pandoc

Step One: Tell Hugo to use Pandoc

Add markup: "pandoc" to your YAML frontmatter.

---
title: "My Blog Post"
date: "2021-03-21"
markup: "pandoc"
---

This blog post will be rendered with Pandoc.

And just like that, Hugo will use Pandoc. But we want to avoid Hugo’s built-in Pandoc command, so we’ll write our own.

Step Two: Write your Pandoc Command

Create a folder called bin at the root of your Hugo project.

Place one of these two files in there – either pandoc.bat for Windows users, or pandoc for Linux/Mac users.

Windows

bin/pandoc.bat

@echo off

%PANDOC_ORIGINAL% --filter .\katex.py

Mac or Linux

bin/pandoc

#!/bin/bash

$PANDOC_ORIGINAL --filter ./katex.py

This is the pandoc command Hugo will end up using, so modify it to suit your needs!

In my case, I use a filter to pre-render the math content on my blog.

Step Three: Create a light wrapper around Hugo

At the root of your project, create a file called hugo.

hugo

#!/bin/bash

export PANDOC_ORIGINAL=$(which pandoc)

# Overrides pandoc.
PATH=$PWD/bin:$PATH

hugo "$@"

Step Four: Set permissions (Mac and Linux Only)

Setting executable permissions.

chmod +x hugo
chmod +x bin/pandoc

(I don’t think this is necessary on Windows.)

Step Five: Run your Hugo script

We’re done! Run these commands to test it out.

# Build the site
./hugo 

# Start the dev server
./hugo server

We just need to call ./hugo to invoke our script (rather than just hugo), but everything else is identical. This includes the use of any subcommands or options the original Hugo takes.

How does this work?

When we created our Hugo wrapper in step three, we modified the PATH.

The PATH determines how the pandoc command finds the actual “Pandoc” program. By putting our bin folder first in the path, we are prioritizing our custom pandoc program (step two) over the “real” Pandoc.

So Hugo will end up calling our custom pandoc, which of course just calls the “real” Pandoc with the arguments we want.

Conclusion

Whenever we depend on a tool, we are subject to both the features and constraints of that tool. Hugo is no exception. However, I’m glad we can leverage Pandoc with a fairly simple workaround, and still get to keep all the benefits of a static site generator.

Hopefully you’re now able to customize a bit more of how your content is rendered with Hugo.