Do you like emacs and try to use it for as much as possible? Do you like static site generators? Then this post may be of relevance to you.

If this all sounds greek to you, and you somehow wandered into here randomly, hoo-boy do I have a fun rabbit hole for you: Getting started with Emacs Doom.

Okay, But Why?

High-level: I really like emacs and writing notes in org-roam, and didn’t want to introduce mental overhead by writing things elsewhere. But I -also- don’t like how the default mode in ox-hugo is a monolithic org file.

For the longest (okay, not -that- long) I tried to use TiddlyWiki for most of my writing, both long and short form. This works well for Zettelkasten style notes, but I felt like it didn’t work out that well for anything other than atomic notes. Add to the fact that it’s a bit difficult to edit/make new notes easily outside of TiddlyWiki, I decided to move my note-taking to org-mode and emacs.

I’m not a strict FOSS purist, so I still tend to like glamming things up with javascript (other people’s javascript), but I just love how fun it is to tinker with emacs configs. I still plan on using TiddlyWiki as display for my zettelkasten, but I wanted something nicer for my tutorials, portfolios, What articles? Rude.

I was aware of Org-roam due to sheer popularity / stars on github and I decided to try it out. I’m (as of writing) not currently using anything super fancy, I’ve basically adapted Jethro Kuan’s guide on taking notes. I figured that since he created the thing, he’d be a good template to follow. I reccomend skimming his article for the basics if you’re new to this.

The second key part of this is ox-hugo, which can export .org files into markdown, which are served by hugo, a popular static-site generator. I’m not entirely sure if there’s any other options, but ox-hugo is by far the most popular that I could find, with easy setup and theming.

The basic workflow idea is, for any article capture, add the appropriate hugo headers and export to markdown to build your site. Simple as that. I’ll go into a bit more detail further down, but it really is that simple. I got my site working in less than an hour. I spent most of my time tinkering around with the javascript to get it looking like how I wanted to. But, also, a lot of the neat stuff I liked from TiddyWiki is pretty replicatable out-of-the-box depending on theming.1

Getting Started

I use Doom Emacs, which makes setup easy. You simply need to enable the appropriate sections in doom’s init.el. It shouldn’t be too difficult to do this in vanilla emacs, but I leave that as an exercise to the reader.

org language section of doom emac's init.el
Doom Emacs' org-language init.el

Config Settings

This is really the main part of the workflow. The key thing to note that is that ox-hugo would prefer you to do everything in a monolithic org file, essentially treating each heading as an article. That kind of conflicts with how Jethro does his notes as I understood things.

But, no worries, I say! It’s simple to just use the org-templates to capture the needed tags. (And most of the benefits of monolith aren’t as helpful unless writing successions of articles where inheriting headers is important.)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
;; config.el, setting templates for org-roam
(setq org-roam-capture-templates
      '(("m" "main" plain
         "%?"
         :if-new
         (file+head "main/${slug}.org"
          "#+title: ${title}
          #+created: %U
          #+last_modified: %U\n\n")
         :immediate-finish t
         :unnarrowed t)
        ("r" "reference" plain
         "%?"
         :if-new
         (file+head "reference/${title}.org"
                    "#+title: ${title}
                    #+created: %U
                    #+last_modified: %U\n\n")
         :immediate-finish t
         :unnarrowed t)
        ("a" "article" plain             ;;                (article)
         "%?"
         :if-new
         (file+head "articles/${title}.org"
                   "#+HUGO_BASE_DIR: ~/code/justin.vc
                   #+HUGO_SECTION: ./posts
                   #+HUGO_AUTO_SET_LASTMOD: t
                   #+TITLE: ${title}
                   #+DATE: %U
                   #+HUGO_TAGS: article
                   #+HUGO_DRAFT: true\n")
         :immediate-finish t
         :unnarrowed t)))

If you’re familiar with or skimmed the earlier linked site, this is simply borrowing Jethro Kuan’s capture templates, -but- adding the necessary headers to utilize ox-hugo. article is where it starts, as I primarily intend to export “articles” as my hugo pages.

HUGO_BASE_DIR - So, this is where you put your hugo site. In this case, it’s a folder for my website, justin.vc. If you’re new to hugo, you can download hugo and create a new site in a folder of your choosing with hugo new site /path/to/site, then simply choose said folder in your template.

HUGO_SECTION - This is a bit self-explanatory, it’s just the section where your posts will go. In essence, ending up in somewhere such as justin.vc/posts/blah on the web, and in justin.vc/content/posts/blah.md file-wise.

These are the two mandatory headers, there’s some clashes between naming in Jethro’s examples, e.g. created -> DATE, but I figure it probably doesn’t matter, since the things in main/references don’t go to hugo.

Workflow

In effect, after you’ve got the template done, you’re pretty much good to go. You can run org-hugo-export-to-md and it’ll automagically create a markdown copy of your post in the appropriate place.

I’m not an expert, of course, but here’s some tips I noticed and some dumb things I wasted time on.

  1. If you notice above, there’s HUGO_DRAFT, make sure to mark that as false (or get rid of it entirely if need be) when ready to publish. Otherwise hugo tends not to build things unless you explicitly tell it to.
  2. ox-hugo knows how to take care of images magically, for single-post org files you simply put them in an appropriate spot (I have them in my ~/.org/roam/articles/static folder, to keep them aligned with my posts) and ox-hugo will move them to -your code folder-/static/picture.png
  3. You can automatically export on save by adding an appropriate footer to the bottom.
Footnotes in org-mode, showing how to auto-save using comment/evals

https://ox-hugo.scripter.co/doc/auto-export-on-saving/

That’s pretty much it! Whenver I want to write an article I just call org-roam-node-find, type in the file name I want, change the title, add tags, and poof.

Tips

  1. Theming is really easy! I really, really, really loathe javascript and even I found it simple to mess with. I personally like PaperModX, a fork of PaperMod that feels a bit nicer.
  2. There’s nothing stopping you from doing other .org files. You simply change the appropriate headers. For example, my blog roll link is simply an export of my elfeed-org file that I use for my RSS feed.
  3. I know I mentioned this is geared towards individual org-notes, but I believe there’s nothing stopping from mixing and matching! I plan on using monolithic org files for certain topics, such as conferences or chains of tutorials.
  4. This is more of an org-tip, but be aware that you can add alt-text to your images with #+ATTR_HTML :alt This is text above an image. I think it helps with SEO, but also it’s useful for those who are hard-of-seeing.
  5. You’re using org-mode, make use of code blocks! Think of all the neat tutorials you could write about literate programming.

Caveats

Of course, this all doesn’t come for free. As ox-hugo uses a parser to move files into markdown, there are a couple features that are a bit, finnicky, as one would say.

The main issue I noticed is shortcodes, there are a lot of in-built shortcodes that work just fine, but once you get into custom ones, that’s where you run into issues.

Example: Sidenote Shortcode

For example, to Such as this. It gets finnicky around ,’s since those are used to seperate the arguments. But it’s still pretty nifty. I -think- you can also do LaTeX in this. Thanks to Danilafe’s blog for figuring this out in pure CSS. I tried to do it with javascript but hugo didn’t like it. I had to make an org-mode macro, and it requires everytime I want to make one, to use syntax such as:

{{{sidenote(f2 "This is inline." "Hello!")}}}

You could see that getting a bit distracting. Thankfully, you can make macros at the global level. I had to google a bit, since it doesn’t seem to come up quickly, but it’s about as simple as setting a org-macro, then creating a snippet.

<span class="sidenote">
    <label class="sidenote-label" for="{{ .Get 0 }}">{{ .Get 1 }}</label>
    <input class="sidenote-checkbox" type="checkbox" id="{{ .Get 0 }}"></input>
    <span class="sidenote-content sidenote-right">{{ .Inner }}</span>
</span>

The Note that the html is 0 indexed, and the macro in org is 1 indexed. This threw me off for a bit.

(setq org-export-global-macros '(
  ("sidenote" . "@@html:{ {% sidenote $1 $2 %} } $3 { {% /sidenote %} }@@")
        ))

This is how you would add a macro at the global level. Replace the spacing in the brackets to the respective {{}}. Hugo didn’t like me replicating shortcode for example purposes.

# -*- mode: snippet -*-
# name: sidenote creation
# uuid: oxhugo_sidenote
# key: snote
# --
{{{sidenote(${1:label}, ${2:inline text}, ${3:sidetext})}}}

This is a snippet for making sidenotes easier.

Conclusions

There are probably some quality of life things I could add such as adding an interactive step to the capture template for tags, and adding in the footer automatically, but I figure I’ll hone my process as I go along, and I’ll try to update this post when it comes to it, or write a new one if necessary.

Do you do something similar, or have any input? Feel free to contact me and let me know, as I said, I’m just kind of bumbling through this. But even writing this article was a piece of cake, so I have good feelings about it and hope to hone this through several iterations of work.


TLDR; make sure capture template is set correctly -> org-roam-node-find -> enter file name -> add tags / change title to more “readable” -> Get writing! -> org-hugo-export-to-md

Post-Update Notes:


  1. Hello! This is a footnote. Yay. In other topics I’ll probably try to limit footnotes to actual citations with citar or the like. ↩︎