Emacs Redux: Meet Utterson, my Jekyll blogging helper

Wait 5 sec.

Back in 2019 I wrote about dealing with Jekyll post URLs,where I shared a tiny command that spared me from having to remember the exactfile name of every article I wanted to link to. That command has been in my configever since, and over the years it quietly grew a few siblings. Recently I finallygathered them all in one place and turned them into a small package -utterson.The common Jekyll tasksI guess anyone who blogs with Jekyll knows the drill. Jekyll is great and I haveno intention of switching, but a few of its conventions generate a steady trickleof manual work: posts live in _posts and their file names have to start with the publicationdate, as in 2026-08-26-some-post.md every article needs a bit of YAML front matter at the top - a layout, a title,a date, some tags linking to another post means using a Liquid tag that wants that date-prefixedfile name, which nobody remembers images and other assets need URLs relative to the site root, not to the articleyou happen to be editingNone of this is hard. All of it is annoying, and all of it is exactly the sort ofmechanical work an editor should be doing on your behalf. So over the years Iwrote a command for each chore, dropped it in my init.el and moved on with mylife.Why I finally cleaned this upThe tipping point was the last command I added - promoting a draft to a post.I write most articles in _drafts, where jekyll serve --drafts shows them but adeploy doesn’t. Publishing one used to mean renaming the file by hand to addtoday’s date, then editing the date in the front matter to match, thenconvincing git that this was a rename rather than a delete plus a brand new file.Three chances to get something wrong, and I certainly got it wrong more than once.Once that was automated I took a look at what had accumulated in my config andrealized it wasn’t a bag of snippets anymore - it was a workflow. And since everyperson blogging with Jekyll from Emacs deals with exactly the same chores, therewas no good reason to keep it to myself. So I spent an evening extracting thelot, wrote some tests, and gave it a name.1There are other Jekyll packages for Emacs, of course. Bothhyde andeasy-jekyll aim to be somethinglike an IDE for your site, with dashboards, deployment and preview buffers. That’sa perfectly reasonable thing to want, it’s just never been what I wanted. My sitelives in git, I deploy it with a push, and I’ve already got Magit and a browser.All I ever wanted was for the bookkeeping to stop being my problem.So utterson is deliberately small. It knows that a Jekyll site is a folder ofMarkdown files with YAML front matter, and that’s about the extent of itsambitions. Renames go through vc, so git records them as renames and the historyof an article follows it around. Nothing in there ever deploys anything.Taking it for a spinThe package is not on MELPA (yet?), so for the time being it’s package-vcterritory:(use-package utterson :vc (:url "https://github.com/bbatsov/utterson" :rev :newest) :custom ;; where to look for sites, when you invoke a command outside one (utterson-search-path '("~/projects/")) :config ;; the commands live in a keymap that you bind wherever you please (keymap-set utterson-mode-map "C-c j" 'utterson-command-map) (global-utterson-mode +1))global-utterson-mode enables the minor mode in the buffers of any folder thathas a _config.yml in it, so the commands are around while you’re working on asite and nowhere else.C-c j p starts a post. It asks for a title, offers a file name slug derived fromit (which you’re free to shorten right there, and I usually do), then asks fortags, completing against every tag the site already uses. That last bit is how youavoid ending up with both Emacs and emacs in your tag cloud. Finally it writesthe front matter, saves the file and leaves the cursor exactly where you’re aboutto start writing:---layout: posttitle: Meet Utterson, my Jekyll blogging helperdate: 2026-08-26 15:01 +0300tags:- Jekyll- Blogs- Packages---That block, as it happens, is the one this very article started with. Here’s thewhole thing in action:C-c j d does the same in _drafts and C-c j t moves an article between thetwo, dating it on the way in and staging the rename with git. C-c j r is for thearticle you wrote a couple of weeks ago and never got around to publishing - itsets the date to now and renames the file to match. Both of them take a prefixargument if you’d rather type a date than accept the current time, which is howyou schedule something for next Monday morning.Publishing a draft looks like this - notice that the file name, the date in thefront matter and git’s idea of what happened all stay in sync:The linking commands are the descendants of that 2019 snippet, all grown up.C-c j u inserts a post_url tag, completing over your posts newest first andshowing you each post’s title next to its file name, and C-c j l inserts theentire Markdown link, already titled after the post it points to:C-c j iinserts an image, C-c j I copies a file into your images folder first (perfectfor a screenshot that’s still sitting on your desktop under a name likeScreenshot 2026-08-26 at 15.01.12.png), and C-c j a links to anything elseunder assets. And C-c j s runs jekyll serve in a buffer, because sooner orlater you do want to look at the thing.Note: There’s a menu as well, in case you’d rather click your way around whilethe keybindings are still settling in.EpilogueIt’s early days for the package, even though most of the code behind it has beenin daily use for years. If you blog with Jekyll from Emacs, I’d love to hear whichof your own little chores it doesn’t cover yet - that’s the fastest way for it toget better.And if you don’t blog with Jekyll - what does your setup look like? I’ve got thefeeling that quite a few of you are writing your posts in org-mode and I’malways curious about the workflows people come up with.That’s all I have for you today! Keep hacking! Gabriel John Utterson is Doctor Jekyll’s loyal friend and lawyer - the manwho keeps his affairs in order. Naming things is hard, but every now and thenyou get lucky. ↩