Blogging Setup: Part 1, Emacs Org mode, ox-hugo and Hugo

2024/11/29

BUILDemacs

I write in Emacs Org-mode. Hugo turns it into a website. ox-hugo bridges the two. Git versions it, GitHub Actions deploys it to S3.

That’s the whole stack. Here’s how to set it up.

What you need

Init Hugo

Create the project

SITE_DIR="/Users/sz/myblog" # your actual path here
hugo new site $SITE_DIR
cd $SITE_DIR
git init

Optionally connect it to a remote: git remote add origin <your-repo-url>.

Add a theme

I use xmin – minimal, no JavaScript, gets out of the way.

cd $SITE_DIR
git submodule add https://github.com/yihui/hugo-xmin.git themes/xmin
mv hugo.toml hugo.toml_bak
cat > config.yaml << EOF
baseurl: "/"
languageCode: "en-us"
title: "SZ"
theme: "xmin"
permalinks:
  post: "/posts/:year/:month/:day/:slug/"
menu:
  main:
    - name: Home
      url: ""
      weight: 1
    - name: About
      url: "about/"
      weight: 2
    - name: Categories
      url: "categories/"
      weight: 3
    - name: Tags
      url: "tags/"
      weight: 4
params:
  footer: "&copy; [SZ](https://example.com) 2024 -- {Year}"
markup:
  goldmark:
    renderer:
      unsafe: true
EOF

Write a test post

cd $SITE_DIR
hugo new content content/posts/my-first-post.md
cat > content/posts/my-first-post.md << EOF
+++
title = "My First Post"
date = 2024-11-30T00:16:00-08:00
tags = ["website"]
categories = ["TECH"]
draft = false
+++

## Introduction
This is **bold** text, and this is *italicized* text.
Here's a [link](https://example.com) for reference.

EOF

Run it

hugo server --port 1111

Hit http://localhost:1111/. You should see a site. It’s ugly. That’s fine – we’re not here for the CSS.

Wire up ox-hugo

Install

M-x package-install RET ox-hugo RET

ox-hugo takes your Org-mode content and exports it as Markdown that Hugo understands. You write in Org, Hugo renders HTML. The two never meet directly.

Pick a structure

ox-hugo supports two modes:

Mode How it works Good for
Subtree-based Multiple posts in one .org file, each as a subtree Small to medium blogs
File-based One .org file per post Larger blogs

We’ll use subtree-based here. One file, many posts.

Create a post

cat > example-post.org << 'EOF'
#+hugo_base_dir: /Users/sz/myblog
;* Posts
;** DONE My First Hugo Post
:PROPERTIES:
:EXPORT_FILE_NAME: my-first-hugo-post
:EXPORT_DATE:  2024-07-12T17:05:38-04:00
:EXPORT_TAGS: blog, emacs
:EXPORT_CATEGORIES: TECH
:END:

This is a sample post created using ox-hugo and Emacs Org-mode.
EOF

A few things to note:

Export

Place your cursor inside the subtree you want to export:

C-c C-e H H

Check content/posts/ for the exported Markdown. Reload Hugo. Your post is live.

That’s the writing loop

Org-mode to ox-hugo to Hugo. Write, export, preview. Part 2 covers getting it deployed – GitHub Actions and S3 – so you’re not manually uploading HTML like it’s 2003.