Hugo is a static website generator written with GO, providing easily capability to convert markdown files into a website. For those who use emacs Org mode , one can use ox-hugo to convert Org files into markdown files that will be compatible with Hugo. Hence the workflow is:
\[\mathrm{Org} \\ \xrightarrow{\text{ox-hugo}} \\ \mathrm{markdown} \\ \xrightarrow{\text{Hugo}} \\ \mathrm{HTML}\]
For me, I use it to create my research journal and personal website.
Here I describe different ways of writing things in Org mode that can be
re-interpreted by ox-hugo to generate the corresponding md files that
can be used by Hugo for my own records.
There are two types of blogging workflow using ox-hugo.
It is generally preferred to do it the second way, i.e. use one Org file for multiple posts where we have one Org subtree per post.
Now Hugo introduces the idea of page bundles for better content management.
There are two types:
See here to see how ox-hugo exports files as bundles.
My personal workflow is to use branch bundle as sections and leaf bundle for specific entries. For example, my typical structure goes like:
content-org/
├── posts/
│ ├── _index.md
│ ├── post1/
│ │ ├── index.md
│ │ └── img1.png
│ └── post2/
│ ├── index.md
│ └── img2.png
└── about/
└── _index.md
where I use branch bundle, using _index.md, to symbolize a section. And then use leaf bundles, using index.md, for each individual post.
Different text formatting are available
* */ /+ +highlight text via ~ ~ or = =A horizontal line can be created for formatting via 5 consecutive dashes.
-----
which looks like
You can create footnotes via the same Org-mode syntax.
Put the footnote as [fn:#] where # is the footnote number.
Then create the definition of footnote separately via
[fn:#] footnote definition1
You can create a quote block via
#+begin_quote
This is a greate quote by me.
-- Me
#+end_quote
which looks like
This is a greate quote by me.
– Me
You can also create code blocks. For example python, we can do
import numpy as np
class Cat:
def __init__(self, name, numWhiskers, weight):
self.name = name
self.numWhiskers = numWhiskers
self.weight = weight
def talk(self):
print("Meow!")
myCat = Cat("Nian", 20, 10)
myCat.talk()
See here for more information.
ox-hugo converts hyperlinks using the standard
Org mode hyperlink syntax :
[[LINK][DESCRIPTION]]
The DESCRIPTION part is optional.
If it is omitted, the LINK itself is displayed on the website.
If both LINK and DESCRIPTION are provided, the website displays
DESCRIPTION, and clicking on it navigates to LINK.
The DESCRIPTION can be plain text or an image.
For example, here is a hyperlinked text to wikipedia .
To display image on the website, we typically want to format it somehow. The common syntax are the following:
#+NAME: IMG_NAME
#+ATTR_HTML: :width 100%
#+CAPTION: Here is a caption
[[IMG_LINK][DISPLAY_IMG_LINK]]
#+NAME is the name used to reference the image via [[IMG_NAME]].
#+ATTR_HTML sets the attributes for html. See here for more info.
And the main one to use is :width, which controls the width of the
image displayed. Lastly we have #+CAPTION, which writes the caption
under the displayed image. Again, DISPLAY_IMG_LINK is optional,
and if it is omitted, image from IMG_LINK gets displayed.
If it is not omitted, then the website shows image from DISPLAY_IMG_LINK,
and clicking on it navigates to IMG_LINK.
For example, Figure 1 is an image via an external link.

Figure 1: Carina Nebula by James Webb Telescope
Figure 2 is where both IMG_LINK and DISPLAY_IMG_LINK are
set to the external image link. Hence clicking on this image
navigates to the original website.

Figure 2: Pillar of Creation by James Webb Telescope
We often want to display local images instead of online images.
Hence we need to link the local images.
Let’s first discuss how Hugo handles it, and then talk about
how ox-hugo can be integrated into the workflow along with Org mode.
Hugo generally has two options for storing images that will
be accessible and linked by md files stored in content/.
See here for some good explanation.
static/ directory.index.md that links the images.By default, the html files that Hugo generates live in the public/
directory, and that serves as the root directory.
Hugo also copies all subdirectories that lives in static/ to
public/, so the conventional way of storing images is to
store them in static/, or perhaps create a subdirectory, say images/
and put images there. Suppose the Hugo base directory is named as hugo,
then the structure goes like
hugo/
├── other subdirs
└── static/
└── images/
└── img1.png
Then the image can be accessed via the following (see here for more information),
which is to basically access via absolute path after the website is built,
where it is assumed that the public/ directory acts as the root directory for
the website.
[[/images/img1.png]]
i.e.

Figure 3: Nian Nian raising her hand (feet)
This has two major downsides:
Now the second way of accessing images, using leaf bundles is my preferred way. A leaf bundle is basically a post that uses the name index.md to store the text. Now we can store images in the same directory as index.md and then we will be able to access it via relative link.
For example, if the structure goes like:
hugo/
├── other subdirs
└── content/
└── posts/
└── post1/
└── index.md
└── img1.png
Then we can access it in index.md via

However, for Org mode users, we typically only want to work with content-org/
directory, and have ox-hugo to auto generate everything to content/.
This means that we wish to work with a structure like:
hugo/
├── other subdirs
├── content
└── content-org/
└── posts/
└── post.org
└── post1/
└── img1.png
where post.org holds all possible post entries. Suppose that we have
a single post named as post1, and it uses image img1.png. Now we can access
this image in the Org file using the relative link.
[[file:post1/img1.png]]
However, Hugo won’t recognize this since img1.png does not belong to the same
directory as index.md which will be auto generated by ox-hugo.
To solve this issue, ox-hugo has a unique feature which will auto copy local attachments
with extensions listed in org-hugo-external-file-extensions-allowed-for-copying.
(see here for more information).
For example, I have
(setq org-hugo-external-file-extensions-allowed-for-copying
'("JPG" "PNG" "JPEG" "GIF" "SVG" "PDF" "MP4"
"jpg" "png" "jpeg" "gif" "svg" "pdf" "mp4"))
Now if the Org post is leaf bundle, i.e. for the Org subtree, we set the output file name to be index.md and set the directory where the bundle lives, i.e. post1.
:EXPORT_FILE_NAME: index
:EXPORT_HUGO_BUNDLE: post1
Now we can simply reference the image as following,
and img1.png will then be auto-copied to content/post/post1/img1.png.
[[file:post1/img1.png]]
i.e.
Figure 4: This is Nian Nian
Note that using the prefix file: is needed to use local
link for DISPLAY_IMG_LINK for local image files in order
to properly display the image. It is optional for IMG_LINK
but it doesn’t hurt. So generally speaking, always start
your local image link with file:.
There are different ways to write math expressions. See here for more info.
$ EXPRESSION $, e.g. $ e=mc^2$$$ EXPRESSION $$, e.g.
\[ i\hbar \frac{\partial}{\partial t} \Psi(\mathbf{r}, t) = \hat{H} \Psi(\mathbf{r}, t) \]\begin{equation}
\label{eq:euler} \tag{1}
\frac{\partial }{\partial t}\left( \rho \vec{U} \right) + \nabla \cdot \left(\rho \vec{U} \otimes \vec{U} \right) + \nabla p = 0
\end{equation}
\begin{equation} \label{eq:euler} \frac{\partial}{\partial t}\left( \rho \vec{U} \right) + \nabla \cdot \left(\rho \vec{U} \otimes \vec{U} \right) + \nabla p = 0 \end{equation}
If you wish to reference the equation, you should use LaTeX environment and
you can reference it via \ref{label}, where label is the label you set via \label{}.
For example, here is Eq. \ref{eq:euler}.
Note that to get this working, the ams tag needs to be present
in your MathJax setting, which is typically stored in math.html.
My math.html looks like:
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>
<script>
MathJax = {
tex: {
tags: 'ams',
displayMath: [['\\[', '\\]'], ['$$', '$$']], // block
inlineMath: [['\\(', '\\)'], ['$', '$']] // inline
}
};
</script>
To preview compiled LaTeX within Org mode, one can use command C-x C-l.
The footnote definition appears at the bottom of the page. ↩︎