Hacking on my blogging workflow

by Jonathan Frederickson — Sat 13 December 2025

Starting December Adventure off with a little meta this year (and a bit late, whoops!). Since setting up this blog, it's always felt like a little too much friction to start a new blog post. It's not too bad, but it does involve creating a file in the posts directory with a given name, manually typing out the timestamp.

I'm always writing my blog posts in emacs anyway, and emacs is the most hackable of editors. So I figured, why not have a template to make things easier?

I'm a heavy user of org-capture to quickly add entries to my TODO list. Although my blog posts are markdown rather than org-mode (for now?), org-capture seemed flexible enough to do the job. And sure enough, it was!

It felt a little gross to do this, but I ended up setting a global variable for the post slug. I tend to use the slug as the filename of each post file, and I didn't want to be prompted for the slug twice. Org-capture can accept a function that controls the file path to capture to; having that function prompt me and save the result seemed like an okay solution.

(defun jfred/capture-blog-post ()
  (interactive)
  (let ((name (read-string "Post slug: ")))
    (setq jfred-capture-blog--slug name)
    (expand-file-name (format "%s.md"
                              name) "~/src/website/posts")))

After that, it was a matter of defining a plain org-capture template to start a new file when captured. I'll just show you all my capture templates so you have the new one in context:

(setq org-capture-templates
      '(("t" "Todo" entry (file "~/org/Inbox.org")
         "* TODO %?\n  %a")
        ("tn" "Todo - no link" entry (file "~/org/inbox.org")
         "* TODO %?")
        ("j" "Journal" entry (file+olp+datetree "~/org/journal.org")
         "* %?\nEntered on %U\n  %i\n  %a")
        ("b" "Blog post" plain (file jfred/capture-blog-post)
         "title: %^{title}
slug: %(format jfred-capture-blog--slug)
tags: 
date: %<%Y-%m-%d %H:%M:%S>
---
%?")
        ("m" "Meeting Notes" entry (file+olp+datetree "~/org/meetings.org")
         "* Meeting Notes - %?")))

(As an aside, I just realized while looking at these that the tn capture doesn't actually work and would be useful. Guess I'll fix that up soon!)

I can tell that the org-capture template system is really meant for templating org files, but it worked here. There's a template string representing a timestamp, but it defaults to the format that org-mode expects; if you want a timestamp in a specific format, you have to use more explicit formatting. But you can do that!

That's all for today. Today's the first day of my holiday break, so I hope to have more time to work on side projects for the rest of the month. My goal this holiday season is to get guile-horton finally supporting persistence. It's not very useful without it, but I did a few things that were perhaps a bit too clever when originally writing it, so it'll likely be annoyingly complicated to get that working. Fingers crossed!


Comment: