67 KiB
Ox-Hugo Manual
- Changelog
- COMMENT v
- v0.7 <2017-12-18 Mon>
- v0.6 <2017-11-09 Thu>
- v0.5 <2017-11-06 Mon>
- v0.4.1 <2017-10-29 Sun>
- v0.4 <2017-10-28 Sat>
- v0.3.2 <2017-10-24 Tue>
- v0.3.1 <2017-10-19 Thu>
- v0.3 <2017-10-18 Wed>
- v0.2.3 <2017-10-11 Wed>
- v0.2.2 <2017-10-10 Tue>
- v0.2.1 <2017-09-28 Thu>
- v0.2 <2017-09-27 Wed>
- v0.1.3 <2017-09-13 Wed>
- v0.1.2 <2017-09-12 Tue>
- v0.1.1 <2017-09-11 Mon>
- Homepage
- Test Site
- Issues/Requests
- Changelog
- Reused Sections
- Menus
- Early Questions
- Getting Started
- Example
- Org to Hugo
- Enhancements
- Meta
- Footnotes
- COMMENT Local Variables
Changelog
COMMENT v
Features
Backward-incompatible changes
Fixes
Meta
v0.7 <2017-12-18 Mon>
Features
- Now C-c C-e H H works for both per-subtree and per-file flows –
commit
. - Support Org heading based internal links –
#88
. - Support list values for custom front-matter variables –
#99
. - Support specifying multiple hugo output formats .. Now the
outputsfront-matter variable is a list. - Support the Org
#+AUTHORand#+CREATORkeywords and their respective Org Export Options –#106
. - Support Org Export Snippets and Export Blocks –
commit
. - Now post titles can be set to
nili.e. be not be a part of the front-matter .. because you can. - Improve the messages printed by
ox-hugoon doing per-subtree or per-file exports.. the progress of files exported using per-subtree flow is now clearer, and the name of the file exported using per-file flow is now explicit.. Helps when you batch export a dozen files with a mix of these 2 flows.
Backward-incompatible changes
- Obsolete org-hugo-export-subtree-*{} functions and replace them with
org-hugo-export-wim-*{} (What I Mean) functions. See the doc string
of
org-hugo-export-wim-to-mdfor details. If you are using the Auto-export on saving flow, note the function name change there too!
Fixes
- Fix number of backticks in code fence when code contains code fence (pathological corner case).
- Better document the
HUGO_CODE_FENCEkeyword –#102
. - Don't render
(c),(r),(tm)inside Latex equations –#104
(Upstream bug workaround). - Better recognition of TOML-compatible integers and floats in meta data for front-matter so that valid integers/floats don't get unnecessarily double-quoted.
Meta
- Add a debugprint.html">
debugprint.htmlpartial to help pretty-print various Hugo objects like Page Params, File and SiteInfo for debug on the test site. - The test site now has tags and categories pages.
- Add few real world example posts containing complex Latex equations: 1, 2.
- Turns out
ox-hugoworks on emacs 24.4 too (but please upgrade to the latest Emacs and Org stable versions!).
v0.6 <2017-11-09 Thu>
Features
- Support the
numexport option. Now you can prefix all post headings (or some not.. the ones withUNNUMBEREDproperty set tot) with their section numbers –#76
. - Org TOC's are now exported as unordered Markdown lists. This allows having TOC's with unnumbered headings too! This also enables prefixing the section headings with their full section numbers, and also having only selected headings unnumbered (both in the post body and the TOC).
- Add support for exporting internal links to source blocks, tables
and images by their block names! –
#29
. - Org table column alignment markers (
<l>,<r>,<c>) are now exported to equivalent Markdown tables.. so a center-aligned column in Org buffer will remain center-aligned in the final HTML too! –#95
. - Allow setting multiple Hugo aliases for a post. Also infer the
section name from inherited
HUGO_SECTIONvalues (subtree-based exports) for those alias prefixes. - Prevent a footnote ref to appear by itself on a newline (based on
wrapping) in the browser –
#96
. - If Hugo shortcodes are used specifically in Markdown (
md) source blocks, they will be auto-escaped (useful when you want to document/talk about some Hugo shortcode in a blog post) –#94
. - If an Org table has just 1 row, don't make it render as a header row in the final HTML.
- If you have a case where you need to have an Org source block
instead a quote block, and then a source block after that quote
block (I know, a very common case.. 😉), Blackfriday barfs
(
Blackfriday
). But we now have a workaround, which just works –#98
. - Now
ATTR_HTMLabove even hyper-linked images works (earlier it worked only above non-hyper-linked images).
Backward-incompatible changes
- Org TOC's are exported as unordered Markdown lists instead of
ordered Markdown lists, and now full section numbers (like 1.2.3)
are shown in the TOC instead of just the last digit (like 3.) –
commit
. - The
numOrg export option is default tonil(only forox-hugo). So Org TOC's are exported without section numbers by default. To get section numbers, setnumtotoronlytoc.
Fixes
- Now exporting 1-row Org tables works too.
- Add missing http/https/ftp prefix for hyper-linked images.
Meta
- Add documentation on how you can have
#91
n-content,Images live in the same directory as Org source)}}} –#91
. - Now only Org files for the documentation site need to be committed
to git.
ox-hugothen exports those to Markdown, and then Hugo publishes those to HTML (as before) — all on Netlify. - Be sure to check out the moderately revamped Test Site. That might
be of interest even if you want to check out what the new features
and changes look like, without first installing/updating
ox-hugoyourself 😄.
v0.5 <2017-11-06 Mon>
v0.4.1 <2017-10-29 Sun>
Features
- Support specifying the
:heightparameter in the#+ATTR_HTMLabove image links. That eventually gets transformed to theheightparameter in thefiguretag in the HTML generated by Hugo. This feature requires building Hugo from its master branch with commit 488631fe (or Hugo v0.31+).
Fixes
- Fix
EXPORT_HUGO_SECTIONnot getting inherited#90
.
v0.4 <2017-10-28 Sat>
Backward-incompatible changes
- Restore the default Org behavior of
#+TAGS. Now that keyword (and theEXPORT_TAGSproperty) is not used byox-hugo. Fixes#89
. - File-based exports must now use
#+HUGO_TAGSto set the post tags. -
Subtree-based exports can use the
EXPORT_HUGO_TAGSproperty to override Org-style tags on the same headline (and the ones inherited from Org-style tags from any of the parent subtrees and#+FILETAGS).- Note that for subtree-based exports,
#+FILETAGScan be used to set tags globally in the file. Earlier#+TAGSwas used for that purpose.
- Note that for subtree-based exports,
-
Subtree-based exports can use the
EXPORT_HUGO_CATEGORIESproperty to override Org-style categories (tags with "@" prefix) on the same headline (and the ones inherited from Org-style categories from any of the parent subtrees and#+FILETAGS).- Note that for subtree-based exports,
#+FILETAGScan be used to set categories (tags with "@") globally in the file.
- Note that for subtree-based exports,
See the new section added to documentation:
Features
- Support specifying the
:widthparameter in the#+ATTR_HTMLabove image links. That eventually gets transformed to thewidthparameter in thefiguretag in the HTML generated by Hugo.
v0.3.2 <2017-10-24 Tue>
Fixes
- Fix issue with headline metadata parsing (ALLTAGS, CLOSED, TODO)
when a post Org heading was immediately followed by that post's
sub-heading. This issue was seen in subtree-based exports
#87
.
v0.3.1 <2017-10-19 Thu>
Fixes
- Fix the source block line number annotation when the line numbers increased in number of digits in the same code block.
v0.3 <2017-10-18 Wed>
Features
- Source blocks can now be exported with line numbers and/or highlighting! See for details.
v0.2.3 <2017-10-11 Wed>
Fixes
org-hugo-slugearlier stripped off only thecodeHTML tag (<code> .. </code>) from the input string, if present. Now it does that for any HTML tag, likespan. For example, this HTML gets stripped off from the above heading (only insideorg-hugo-slugwhen deriving the slug string): ~<span class="timestamp-wrapper"><span class="timestamp"><2017-10-11 Wed></span></span>~.
v0.2.2 <2017-10-10 Tue>
Backward-incompatible changes
- Now
ox-hugoby default requires text, to be sub/super-scripted, to be wrapped in{}. So nowa_bwill be exported asa_b, buta_{b}will be exported asa<sub>b</sub>. To revert back to the earlier behavior, user needs to add#+OPTIONS: ^:tto their Org file.
v0.2.1 <2017-09-28 Thu>
v0.2 <2017-09-27 Wed>
Features
- Add support for all Hugo
figureshortcode parameters#79
. - New option
org-hugo-delete-trailing-wsdefaults tot; now Hugo deletes trailing white-spaces by default. - New options
org-hugo-default-static-subdirectory-for-externalsandorg-hugo-external-file-extensions-allowed-for-copying(related to#69
).
Fixes
Backward-incompatible changes
- Switch the default value of
org-hugo-use-code-for-kbdoption tonil[88ba15ae].
v0.1.3 <2017-09-13 Wed>
- Now a HUGO key value set to
"nil", like#+HUGO_CODE_FENCE: nil, will evaluate as nil instead of t, as noworg-hugo--plist-get-true-pis used to parse boolean keys instead ofplist-get.
v0.1.2 <2017-09-12 Tue>
- Make DateTime matching better; new internal variable
org-hugo--date-time-regexp. Earlier time zones ahead of UTC (with+sign) were not detected as dates inorg-hugo--quote-stringand thus were unnecessarily quoted.
v0.1.1 <2017-09-11 Mon>
- Use CLOSED log drawer info if available to set the date in
front-matter
#68
. - Code optimization: Use of
org-entry-getat places instead of maintaining global variables.
Homepage
Screenshots
Installation
Usage
Thanks
Test Site
Issues/Requests
Changelog
Reused Sections
Introduction
ox-hugo is an Org exporter backend that exports Org to
Hugo-compatible Markdown (Blackfriday) and also generates the
front-matter (in TOML or YAML format).
This project consists of ox-blackfriday.el too. It is a derivation
of ox-gfm">ox-gfm with support added for Blackfriday Markdown tables and
many other tweaks. ox-hugo backend extends from this.
Installation
This package requires emacs 24.5+ and Org 9.0+. It is available on Melpa (https://melpa.org/#/ox-hugo).
COMMENT Usage
Once the package is installed, you will need to require it so that
the ox-hugo export options are available in the Org Export
Dispatcher menu (the one you see when you hit C-c C-e to initiate
any export).
You can do that by adding the below to your config:
(with-eval-after-load 'ox
(require 'ox-hugo))
If you use use-package, you can do the below instead:
(use-package ox-hugo
:after ox)
Spacemacs
Spacemacs users can choose to add this snippet to their
dotspacemacs/user-config function in .spacemacs:
(defun dotspacemacs/user-config ()
;; Other stuff
;; ..
;; ox-hugo config
(use-package ox-hugo
:ensure t ;Auto-install the package from Melpa
:after ox))
If you do so, you also need to add ox-hugo to
dotspacemacs-additional-packages.
Verified to work on Spacemacs develop branch with spacemacs-base
distribution, emacs editing style.
Before you export
Before you export check that these properties are set as you need:
- HUGO_SECTION
- The default Hugo section name for all the posts. See
here for more information on Hugo sections. It is
common for this property to be set to
postsorblog. The default value is set usingorg-hugo-default-section-directory. - HUGO_BASE_DIR
-
Root directory of the source for the Hugo site. If this is set to
~/hugo/, the exported Markdown files will be saved to~/hugo/content/<HUGO_SECTION>/directory. By default, the Markdown files reside in a hierarchy under thecontent/directory in the site root directory (ref). If you try to export without setting this property, you will get this error:user-error: It is mandatory to set the HUGO_BASE_DIR property
Important: If you choose to export an Org subtree as a post, you
need to set the EXPORT_FILE_NAME subtree property. That property is
used by this package to figure out where the current post starts.
Export bindings
The common ox-hugo export bindings are:
For both one-post-per-subtree and one-post-per-file flows
-
C-c C-e H H -
Export "What I Mean".
- If point is in a valid Hugo post subtree, export that
subtree to a Hugo post in Markdown.
A valid Hugo post subtree is an Org subtree has the
EXPORT_FILE_NAMEproperty set. - If the file is intended to be exported as a whole (i.e. has the
#+TITLEkeyword), export the whole Org file to a Hugo post in Markdown.
- If point is in a valid Hugo post subtree, export that
subtree to a Hugo post in Markdown.
A valid Hugo post subtree is an Org subtree has the
-
C-c C-e H A -
Export all "What I Mean"
- If the Org file has one or more 'valid Hugo post subtrees', export them to Hugo posts in Markdown.
- If the file is intended to be exported as a whole (i.e. no 'valid
Hugo post subtrees' at all, and has the
#+TITLEkeyword), export the whole Org file to a Hugo post in Markdown.
For only the one-post-per-file flow
-
C-c C-e H h - Export the Org file to a Hugo post in Markdown.
Customization Options
Do M-x customize-group, and select org-export-hugo to see the
available customization options for this package.
Screenshots Intro
Before you read further, you can see below how ox-hugo translates
Org to Markdown (Org on the left; exported Markdown with Hugo
front-matter on the right).
Screenshots
Editorial
The preferred way to organize the posts is as Org subtrees (also the main reason to write this package, as nothing like that was out there) as it makes the meta-data management for Hugo front-matter pretty effortless.
If you are a one Org-file per post type of a person, that flow works
too! Just note that in this flow many of those #+HUGO_ properties
need to be managed manually.. just as one would manage the front-matter
in Markdown files — See the Org versions in the above screenshots for
comparison.
Demo
Org source → ox-hugo Exported Markdown">ox-hugo Exported Markdown → https://ox-hugo.scripter.co/test
Now, the test site looks very simple, because:
- It is designed to verify if all the content translates from Org to Markdown as expected.
- It uses a bare_min theme written just for the debug purpose (not extra aesthetics).
See Hugo Themes for examples of really good site prettification and presentation styles.
Actual usage examples
- https://ox-hugo.scripter.co –
ox-hugoDocumentation Site - https://scripter.co – My blog
Note to Future Contributors
NOTE TO FUTURE CONTRIBUTORS: I plan to merge this package into GNU Elpa or Org source at some point.
So you will need to assign your copyright to FSF in order to get your patches accepted.
As a bonus, once you have assigned your copyright to FSF, doors open up for your future contributions to Emacs too!
Contribute to documentation
- Clone this repo.
- Add/edit documentation to
doc/ox-hugo-manual.org. -
Run
make doc.- This generates the Markdown files for the
ox-hugodocumentation site and the .org files likeREADME.organdCONTRIBUTING.orgfor GitHub.
- This generates the Markdown files for the
- Review the changes in the generated .org and .md files.
-
Commit only the .org files, push branch and create PR.
- The
.org→.mdconversion is done byox-hugoon Netlify.
- The
Contribute to code
- Clone this repo.
- Add/edit the .el files, tests in
test/site/content-org/, and documentation todoc/ox-hugo-manual.org. - Run
make md doc. -
Review the changes in the generated .org and .md files.
- Ignore the changes shown in
git diffrelated to only the randomly generated Org ID's (likeorg17de7a9).
- Ignore the changes shown in
-
Commit (don't push your branch yet!).
- As mentioned above, commit only the .org files for documentation.
- Run test:
make test(you need togit commiti.e. do the above step before this step). - Fix your commit(s) if the test fails.. repeat till you succeed.
- Push your feature branch and create PR.
Debug
If the ox-hugo exports do not work as expected, or if you get an
error backtrace,
- Open an Issue.
- Describe the problem you are seeing.
-
Provide the debug info:
- Do
M-x org-hugo-debug-info, That will copy the debug info in Markdown format to the kill ring. So do not manually do any other kill (copy/cut) operation after that! -
Paste the Markdown contents in the GitHub issue.
- You can still hit the Preview tab of the Issue before submitting it.
- Do
Test
- Clone this repo.
- Run
make test.
Thanks
- Matt Price (@titaniumbones)
- Puneeth Chaganti (@punchagan)
- Also thanks to holgerschurig.de, whyarethingsthewaytheyare.com and
the goorgeous">
goorgeousproject by Chase Adams (@chaseadamsio) for inspiration to start this project.
Menus
Early Questions
Why ox-hugo?
> A picture is worth a thousand words.
So let me show you this screenshot that took on my phone (running Emacs in Termux app on Android), showing the single Org file that serves as a source of this whole documentation site.

Using Org just as a markup like Markdown is a miniscule part of its complete feature-set. Org also allows stuff like:
- Easy ordering/manipulation/commenting of subtrees
- Creating tables (with even formulas like in Excel)
- Directly including source code snippets from external files (instead of having to copy/paste them in)
- Running code snippets within the Org file and embedding the results (Org Babel)
- ..
Using Org for content writing allows using in-built Org features to translate to Hugo front-matter:
- Org uses an outline structure and can inherit meta data (tags and properties) from one subtree to children subtrees.
- Using that feature, one can tag one tree as emacs, and everything under that tree (all posts under that) will get that tag automatically.
- The same concept applies to inheriting any Org property meta data like menu entry, category, section name, etc.
- A subtree can be quickly marked to be in TODO state (default binding
C-c C-t). A TODO post is marked as a draft Hugo post. - The menu-item weights and/or post weights can be set to be auto-calculated so that the menu items or post order in the final HTML appear in the same order as the respective subtrees in Org. If the subtrees are re-ordered in Org, the weights get changed too.
- One can have a subtree with section property set to "posts" and all post subtrees under that will go to that section. Similarly another parent subtree can have that property set to "articles", and so on.
- Images can be displayed inline in the Org buffer.
- After save hooks can be set up in Emacs so that each time I save the
file, only the current subtree in Org gets exported to
Markdown. With the Hugo server running with the new switch that auto
changes the preview to the last changed post (
--navigateToChangedintroduced in Hugo 0.25), the flow is seamless – Save the Org file and see the exact changed post in browser. - All posts can simply be subtrees in a single Org file. That way
one can take advantage of Org subtree filtering and searching
functions (
org-sparse-treebound toC-c /by default). - (and much more..)
Do I need to re-write my whole blog in Org?
If you are considering to try out ox-hugo, and if you have already
been using Hugo, it is normal for this thought to cross your mind:
I already have dozens or hundreds of posts written in Markdown. Do I need to convert them to Org if I want to start using
ox-hugo?
The answer is No.
This package will export your future posts written in Org to
Markdown. And those files will live along with your already written
Markdown posts. So converting existing Markdown files to Org would be
purely the user's choice, your choice – but that's by no means a
necessity if you want to start using ox-hugo.
.. And if at some point, you want to stop using ox-hugo, you still
have the exported Markdown files.
Getting Started
Installation
Usage
How do I try ox-hugo on my site?
cdto your Hugo site base directory – the one that contains theconfig.toml(orconfig.yamlorconfig.json).-
Start the
hugo serverin that directory:hugo server -D --navigateToChanged
--navigateToChangedrequires Hugo 0.25+.
- Above command will mention the localhost where the site is served. Open that in your browser.
-
Create a separate directory for Org content in the Hugo site base directory. You can name it anything, but I prefer to name it
content-orgExamples —
- Create an Org file in there and follow the section to export it.
Blogging Flow
There are 2 major blogging flows that can be used with this package:
-
One post per Org subtree (preferred)
- Export only the current post Org subtree, or
- Export all valid Hugo post subtrees in a loop.
-
One post per Org file
- This works but you won't be able to leverage Org-specific
benefits like tag and property inheritance, use of TODO states to
translate to post
draftstate, auto weight calculation for posts and menu items, etc.
- This works but you won't be able to leverage Org-specific
benefits like tag and property inheritance, use of TODO states to
translate to post
See the
page to see how to quickly create new posts.See the
page to learn how to setup up seeing live-preview of the Hugo-rendered HTML each time you doC-x C-s in the Org file. That section explains how
to set that up for either of the above two blogging flows.
Example
Screenshots
Hugo test site for this package
A site with bare-bones Hugo "theme" is used to live-test the
package — you'll know why theme is double-quoted once you try out the
site on hugo.
Check out the example single Org file. That is created for testing various Org->Hugo content and meta-data translation features. Here are the exported Markdown files.
How to try ox-hugo on that site?
-
Clone this repo and do:
make serve HUGO_BASE_DIR=test/site
- Requires Hugo 0.25+ for
--navigateToChangedswitch.
- Requires Hugo 0.25+ for
- Open
http://localhost:1337in your browser. - In a different terminal,
cdto the repo directory. -
Run:
make md
- In few seconds, over a hundred test posts will get created, with
the
hugo serveraided preview in the browser zapping through each new created post.
Alternative way
- Clone this repo.
-
cdto thetest/site/directory and do:hugo server -D --navigateToChanged
--navigateToChangedrequires Hugo 0.25+.
- Above command will mention the localhost where the site is served. Open that in your browser.
- In emacs,
(require 'ox-hugo)or evaluate theox-hugo.elfrom the cloned repo. - Open the {{{ox-hugo-test-file}}} file.
C-c C-e H A– That will export all subtrees in the file to Markdown files.- In few seconds, over a hundred test posts will get created, with
the
hugo serveraided preview in the browser zapping through each new created post (because of that new feature--navigateToChangedintroduced in Hugo 0.25).
Org to Hugo
Org meta-data to Hugo front-matter
For subtree exports
When organizing the posts as Org subtrees, many Hugo front-matter variables get set implicitly using the meta-data parsed from the posts in Org.
Below, where subtree is mentioned, it implies a valid Hugo-post
subtree i.e. an Org subtree that has the EXPORT_FILE_NAME property
set.
| Hugo front-matter (TOML) | Org | Org description |
title = "foo" |
* foo |
Subtree heading |
date = 2017-09-11T14:32:00-04:00 |
CLOSED: [2017-09-11 Mon 14:32] |
Auto-inserted CLOSED subtree property when switch to Org DONE state |
date = 2017-07-24 |
:EXPORT_DATE: 2017-07-24 |
Subtree property |
lastmod = <current date> |
:EXPORT_HUGO_AUTO_SET_LASTMOD: t |
Subtree property |
lastmod = <current date> |
#+HUGO_AUTO_SET_LASTMOD: t |
Org keyword |
tags = ["toto", "zulu"] |
* foo :toto:zulu: |
Subtree heading tags |
categories = ["x", "y"] |
* foo :@x:@y: |
Subtree heading tags with @ prefix |
draft = true |
* TODO foo |
Subtree heading Org Todo state set to TODO (or DRAFT) |
draft = false |
* foo |
Subtree heading Org Todo state not set to TODO (or DRAFT) |
weight = 123 |
:EXPORT_HUGO_WEIGHT: auto |
When set to auto, weight is auto-calculated |
weight = 123 (in [menu.foo]) |
:EXPORT_HUGO_MENU: :menu foo |
Menu weight is auto-calculated unless specified |
Notes
- Precedence for
dateparsing:CLOSEDsubtree property more thanEXPORT_DATEsubtree property more than#+DATE:keyword.
For complete-file exports
| Hugo front-matter (TOML) | Org |
title = "foo" |
#+TITLE: foo |
date = 2017-07-24 |
#+DATE: 2017-07-24 |
lastmod = <current date> |
#+HUGO_AUTO_SET_LASTMOD: t |
tags = ["toto", "zulu"] |
#+HUGO_TAGS: toto zulu |
categories = ["x", "y"] |
#+HUGO_CATEGORIES: x y |
draft = true |
#+HUGO_DRAFT: true |
draft = false |
#+HUGO_DRAFT: false (default) |
weight = 123 |
#+HUGO_WEIGHT: 123 |
weight = 123 (in [menu.foo]) |
#+HUGO_MENU: :menu foo :weight 123 |
Notes
- The auto weight calculation for posts and menu items works only
for subtree exports. For the complete-file export flow, one needs to
specify the weights manually. The value of weight set to
"auto"will be equivalent to nil for the complete-file export flow.
Formatting
Below table shows the translation of Org markup to Markdown markup in
the exported .md files.
See the Org source in all-posts.org">all-posts.org under Formatting -> General
heading and how it exports to Markdown in general-formatting.md">general-formatting.md.
| Org | Markdown |
*bold* |
**bold** |
/italics/ |
_italics_ |
=monospace= |
`monospace` |
~key-binding~ |
`key-binding` |
- if org-hugo-use-code-for-kbd is nil [default] |
|
~key-binding~ |
<kbd>key-binding</kbd> |
- if org-hugo-use-code-for-kbd is non-nil |
|
- Requires CSS to render the <kbd> tag as something special. |
|
+strike-through+ |
~~strike-through~~ |
_underline_ |
<span class = "underline">underline</span> |
- Requires CSS to render this underline class as an underline. |
Image Links
This section will provides few alternatives for linking to images in
Org files in a way that's compatible with ox-hugo and Hugo.
For the sake of the below explanation, let's have the HUGO_BASE_DIR
(the directory containing the Hugo site's config.toml file) be
~/hugo/.
In that case, the Hugo static directory will be ~/hugo/static/.
References to files in the static directory
Now if you have a file ~/hugo/static/images/foo.png, ox-hugo makes
it convenient for you to reference that image by simply
/images/foo.png. Note that this is not the default behavior of
other Org exporters.
But in the case of Hugo, as the referenced file will normally be in
the Hugo static directory, ox-hugo allows the /images/foo.png
style of short reference if that is a valid path under that Hugo
site's static directory.
See the below examples on how to reference images in different ways:
Inline image (Unhyperlinked)
[[/images/foo.png]]
Inline image (Hyperlinked to the image itself)
[[file:/images/foo.png][file:/images/foo.png]]
- NOTE
- The
file:prefix has to be used in both Link and Description components of the Org link.
Link to an image (Image not inlined)
[[/images/foo.png][Click here to see foo.png]]
This style of linking will work for references to non-image files in the static directory too.
References to files outside the static directory
This is a unique feature of ox-hugo.
(i) If a reference is made to a file outside the Hugo static
directory and (ii) if it has one of the extensions listed in
org-hugo-external-file-extensions-allowed-for-copying, then that
file is copied by ox-hugo to the static directory.
Here is an example link:
[[~/some-dir/static/images/foo.png]]
Source path contains /static/
If you link to files outside of the Hugo static directory, just
ensure that the path contains the string /static/ if you like to
preserve the directory structure. Necessary directories are then
created inside the static directory to preserve the structure.
Example translations between outside static directory paths to the
copied location inside static:
File location outside static |
Copied-to location inside static |
Explanation |
~/temp/static/images/foo.png |
<HUGO_BASE_DIR>/static/images/foo.png |
If the outside path has /static/ in it, the directory structure after that is preserved when copied. |
~/temp/static/img/foo.png |
<HUGO_BASE_DIR>/static/img/foo.png |
(same as above) |
~/temp/static/foo.png |
<HUGO_BASE_DIR>/static/foo.png |
(same as above) |
~/temp/static/articles/zoo.pdf |
<HUGO_BASE_DIR>/static/articles/zoo.pdf |
(same as above) |
See the
section to learn how to set the HUGO_BASE_DIR.
Source path does not contain /static/
Here is an example link where the source path does not contain
/static/:
[[~/some-dir/bar/foo.png]]
In this case, that file is copied directly to the
org-hugo-default-static-subdirectory-for-externals sub-directory
(ox-hugo/ by default) within the Hugo static directory. No directory
structure generation happens in this case.
File location outside static |
Copied-to location inside static |
Explanation |
~/temp/bar/baz/foo.png |
<HUGO_BASE_DIR>/static/ox-hugo/foo.png |
Here, as the outside path does not have /static/ in it, the file is copied to the ox-hugo/ dir in Hugo static/ dir. |
Disable auto-copying
This auto-copying behavior can be disabled completely by setting
org-hugo-external-file-extensions-allowed-for-copying to nil.. but
you might not want that if you keep your files outside the Hugo
static directory.
Source blocks
ox-hugo tries to generate Markdown with fenced code blocks if
possible. It also supports exporting source blocks with line numbers
and/or highlighting enabled for specific lines.
Code Fences
By default, the HUGO_CODE_FENCE property is set to a non-nil
value. So the code blocks will be exported with GitHub-style
code-fencing with triple-backticks when possible.
Example:
```emacs-lisp
(message "Hello")
```
- Note
- It is necessary to set the Hugo site config variable
pygmentsCodeFencestotruefor syntax highlighting to work for fenced code blocks.
The Hugo highlight shortcode is automatically used instead of code
fences (even with this property at a non-nil value) if:
- Line numbers are enabled using the Org
-n/+nsyntax (see below), or - Line highlighting is enabled using the
:hl_linesparameter in the source block header (see below).
Set the HUGO_CODE_FENCE property to nil if you want to always
use the Hugo highlight shortcode.
Line numbers
Line numbers can be enabled/configured using the Org -n / +n
syntax. See the Info node (org) Literal examples">(org) Literal examples for more
information.
Here are some examples fetched from the "Source blocks with line number annotation" test case in the {{{ox-hugo-test-file}}}.
Highlighting
Implementing this feature was interesting, because while Org doesn't
have a syntax to enable highlighting only specific lines, the Hugo
highlight shortcode does allow that via hl_lines argument.
So the challenge was to present that "lines to be highlighted"
information in the Org source in a nice format and then translate that
to the hl_lines highlight shortcode argument at the time of
exporting.
It involved hacking the org-babel-exp-code. See emacs-orgmode thread">this discussion on
the emacs-orgmode thread if interested.
This feature is implemented by using a parameter called :hl_lines in
the header of source blocks. This parameter is specific to ox-hugo,
and that's why implementing this needed that hack.
If a user wants to highlight lines 1, and then 3 to 5, they would add
:hl_lines 1,3-5 to the source block header.
Without line numbers
With line numbers
The Org source for the below is similar to the above, except that the
-n switch is also added to enable the line numbers.
Tags and Categories
Subtree-based Export
Tags
For subtree-based exports, the Hugo front-matter tags values are
derived from Org tags set for the post subtree headline.
Example:
* My post :tag1:tag2:
By default, Org tags from parent headlines, and the tags set in the
#+FILETAGS keyword get inherited (as the default value of
org-use-tag-inheritance is t). If the tag inheritance doesn't work
as expected, check that the value of that variable is set as required.
If the EXPORT_HUGO_TAGS property is set for a valid Hugo post
subtree, the value of that property will completely override the Org
tags set even on that subtree, the inherited values of Org-style tags
from parent headlines and even #+FILETAGS.
Note that if you want to prevent a file from getting exported, you can
assign a special tag to the whole file (example: no_no_dont_export),
but that tag has to be set using the #+FILETAGS keyword
(#+HUGO_TAGS keyword is only for setting the tags in Hugo
front-matter). This applies to both, file-based and subtree-based1
export flows.
Then in your batch export setup, add that special tag to the
org-export-exclude-tags variable. You can grep through this repo for
the special tag dont_export_during_make_test that is used to mark
few Org files to not be exported during make test.
#+FILETAGS and not #+TAGS?
- About
#+FILETAGS– Tag Inheritance orC-h i g (org) Tag inheritance - About
#+TAGS– Setting Tags orC-h i g (org) Setting tags
Categories
For subtree-based exports, the Hugo front-matter categories values
are derived from Org tags set for the post subtree headline, but only
the ones prefixed with @.
Example:
* My post :@cat1:@cat2:
As with the tags, by default, the categories (Org tags with "@"
prefix) from parent headlines, and the ones set in the #+FILETAGS
keyword too get inherited (as the default value of
org-use-tag-inheritance is t). If the tag inheritance doesn't work
as expected, check that the value of that variable is set as required.
If the EXPORT_HUGO_CATEGORIES property is set for a valid Hugo post
subtree, the value of that property will completely override the
categories set even on that subtree, the inherited values of
categories from parent headlines and even #+FILETAGS.
File-based Export
The tag (and category) inheritance does not apply to the file-based
export flow. So #+FILETAGS will have no effect in this flow.
- To set tags, use
#+HUGO_TAGS. - To set categories, use
#+HUGO_CATEGORIES.
Hyphens in tags (and categories)
Hyphens are not allowed in Org tags. So ox-hugo converts single
underscores to hyphens if org-hugo-prefer-hyphen-in-tags is set to
non-nil (default). So an Org tag abc_def will be exported as tag
"abc-def". Similarly an Org tag @abc_def will be exported as
category "abc-def".
To export a tag or category with an underscore, use 3 consecutive
underscores. So an Org tag abc___def will be exported as tag
"abc_def". If you rather prefer to always export single underscores
as underscores, set org-hugo-prefer-hyphen-in-tags to nil.
This variable does not affect the tags set via #+HUGO_TAGS keyword
or the EXPORT_HUGO_TAGS property, because Org keywords and
properties allow using the hyphen character. So underscores and
hyphens in tags (or categories in #+HUGO_CATEGORIES /
EXPORT_HUGO_CATEGORIES) remain untransformed on export.
Spaces in tags (and categories)
Spaces are not allowed in Org tags. So ox-hugo converts double
underscores to spaces if org-hugo-allow-spaces-in-tags is set to
non-nil (default). So an Org tag abc__def will be exported as tag
"abc def". Similarly an Org tag @abc__def will be exported as
category "abc def".
This variable also affects the tags set via #+HUGO_TAGS keyword or
the EXPORT_HUGO_TAGS property, because it is not possible to
distinguish in Org keywords and properties whether the space is part
of the tag or used to separate two tags. The same applies to
categories set via #+HUGO_CATEGORIES / EXPORT_HUGO_CATEGORIES.
Examples
- Org source
- Exported Markdown – inheriting-tags.md">
inheriting-tags.md, overriding-tags.md">overriding-tags.md - Hugo-generated HTML – Inheriting tags, Overriding tags
Table of Contents
Hugo can automatically parse the Markdown content and auto-create a
Table of Contents. See its documentation on Table of Contents. So
ox-hugo doesn't generate the Org-parsed TOC by default.
The advantage of using Hugo-generated TOC is that it does not clutter the Markdown source.
Though, the advantage of Org-generated TOC is that you get finer control on:
- Where to include the TOC — Location of the
#+TOCkeyword in the Org content. - How many headlines to include in the TOC — Example:
#+TOC: headlines 2or:EXPORT_OPTIONS: toc:2. - Whether you want all the headlines in the TOC to be numbered or
not — See
org-hugo-export-with-section-numbers. - Whether you want only some headlines numbered (both in post body
and the TOC) — Set the
UNNUMBEREDproperty of that headline tot.
If you'd like to use the Org-generated TOC instead of the Hugo-generated one, you can do it one of these many ways:
- The default is to use the Hugo-generated TOC. But that can be
changed by setting
org-hugo-export-with-tocvariable to a non-nil value, liketor2. - Org-generated TOC can be enabled per-post by either setting
EXPORT_OPTIONSsubtree property (for subtree-based exports) or theOPTIONSkeyword (for file-based exports) to a non-nil value, liketoc:tortoc:2. - Above two options will insert the TOC between the front-matter and
the Markdown content. If you'd like to insert the Org-generated TOC
anywhere else in the post, you can do it using the
#+TOCkeyword.. Example:#+TOC: headlines 2.
See Org manual Table of Contents section for more info.
Note that ox-hugo does not support #+TOC: listings and #+TOC:
tables.
Enhancements
Auto-export on Saving
Wouldn't it be awesome if you can see the live-preview of your Hugo-rendered post each time you saved your post in Org?
Well.. you can do that with these steps:
First time setup
after-save-hook setup for both per-subtree and per-file flows
This step applies whether you intend to use the per-subtree export flow or the per-file export flow.
after-save-hook
-
Add below to the very-end of your Org file:
* Footnotes * COMMENT Local Variables :ARCHIVE: # Local Variables: # eval: (add-hook 'after-save-hook #'org-hugo-export-wim-to-md-after-save :append :local) # End:About Footnotes: Here I recommend adding the
* Footnotesheader too so that in case you add any Org footnotes, they go directly to that section you created. Otherwise, Org will auto-create a new Footnotes heading at the end of the file.. and the Local Variables heading would then no longer be at the end of the file. - Then save the file, and do
revert-buffer. - You will be prompted to add that
evalline to your Customize setup, hit!to permanently save that setting and prevent future prompts.
While this sub-step is useful for the per-subtree export flow, it won't hurt to implement even if you are using the per-file flow.
This step is useful if you choose to write new posts using
org-capture as explained in the {{{doc(org-capture-setup,/Org
Capture Setup/)}}} section.
After saving the below to your emacs config and evaluating it, auto-exports will be prevented when saving a new post created using Org Capture.
If you don't do the below, auto-exports would happen as soon as you
C-c C-c on your initial capture.. which could get annoying.
(with-eval-after-load 'org-capture
;; Do not cause auto Org->Hugo export to happen when saving captures
(defun modi/org-capture--remove-auto-org-to-hugo-export-maybe ()
"Function for `org-capture-before-finalize-hook'.
Disable `org-hugo-export-wim-to-md-after-save'."
(setq org-hugo-allow-export-after-save nil))
(defun modi/org-capture--add-auto-org-to-hugo-export-maybe ()
"Function for `org-capture-after-finalize-hook'.
Enable `org-hugo-export-wim-to-md-after-save'."
(setq org-hugo-allow-export-after-save t))
(add-hook 'org-capture-before-finalize-hook #'modi/org-capture--remove-auto-org-to-hugo-export-maybe)
(add-hook 'org-capture-after-finalize-hook #'modi/org-capture--add-auto-org-to-hugo-export-maybe))
Alternative after-save-hook setup only for per-file flow
This step applies only to the per-file export flow.
after-save-hook (only for per-file flow)
If you use a seperate Org file for each blog post, you can add the below to your config instead of doing the above. Skip Step 1b below altogether if you use this alternative.
(use-package ox-hugo
:ensure t
:after ox
:init
(defconst my/hugo-org-content-dir (expand-file-name "~/hugo_base_dir/content-org/")
"Directory containing the Org mode posts.")
(defun my/org-hugo-publish-current-buffer-as-post ()
"Export the current Org file if a valid Hugo post.
Current file is exported using `org-hugo-export-to-md' if it
contains the #+TITLE keyword and is present in the
`my/hugo-org-content-dir'."
(let ((fname (buffer-file-name)))
(when (and fname
(string-match-p (concat "\\`" (regexp-quote my/hugo-org-content-dir) ".*\\.org\\'")
fname))
(save-excursion
(goto-char (point-min))
(if (< (how-many "^#\\+TITLE:") 1)
(message "Unable to export as the Org file is missing the #+TITLE keyword.")
(org-hugo-export-to-md))))))
(defun my/org-mode-hook-fn ()
"My Org mode customization."
(add-hook 'after-save-hook #'my/org-hugo-publish-current-buffer-as-post :append :local))
(add-hook 'org-mode-hook #'my/org-mode-hook-fn))
Steps that might need to be taken every time
Once the initial setup is done, the following steps apply to both blogging flows.
Step 2: Start the engines (Hugo Server)
We start the hugo server so that we can see the live-preview each
time the Org file is saved.
I recommend using Hugo version 0.25 at the minimum as that added
support for the awesome --navigateToChanged switch!
Run below in your Hugo site root (the directory that contains the site
config.toml) to start the server:
hugo server -D --navigateToChanged
Step 3: Open your browser
By default the site is served locally on port 1313 on localhost. So the above step would have printed something like below at the end:
Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
So open your favorite browser pointing to that address.
FINAL step that needs to be taken every time
Step 4: Save and be in awe
If you are like me, you might not need to repeat steps 2 and 3 above,
as you can leave the hugo server running in a separate terminal, and
have a browser tab pinned to that localhost.
So with that, have the emacs and browser frames set up side-by-side, and edit your Org post.
Hit C-x C-s and be in awe as the browser auto-refreshes to the
exact post you modified!
Org Capture Setup
If you do not want to manually type the EXPORT_FILE_NAME for each
new post, here is an example Org Capture template can help:
;; Populates only the EXPORT_FILE_NAME property in the inserted headline.
(with-eval-after-load 'org-capture
(defun org-hugo-new-subtree-post-capture-template ()
"Returns `org-capture' template string for new Hugo post.
See `org-capture-templates' for more information."
(let* ((title (read-from-minibuffer "Post Title: ")) ;Prompt to enter the post title
(fname (org-hugo-slug title)))
(mapconcat #'identity
`(
,(concat "* TODO " title)
":PROPERTIES:"
,(concat ":EXPORT_FILE_NAME: " fname)
":END:"
"%?\n") ;Place the cursor here finally
"\n")))
(add-to-list 'org-capture-templates
'("h" ;`org-capture' binding + h
"Hugo post"
entry
;; It is assumed that below file is present in `org-directory'
;; and that it has a "Blog Ideas" heading. It can even be a
;; symlink pointing to the actual location of all-posts.org!
(file+olp "all-posts.org" "Blog Ideas")
(function org-hugo-new-subtree-post-capture-template))))
Above capture will auto-insert a heading prefixed with TODO. With
org-log-done set to 'time, on changing the TODO state to the
DONE state (C-c C-t), a Special Property called CLOSED will be
auto-inserted below the heading. Below is an example.
*** DONE Narrowing the Author column in Magit :org:log: CLOSED: [2017-12-18 Mon 16:36]
ox-hugo auto-sets the date field in the front-matter to the time
stamp in that CLOSED property.
Alternative way to set the date field
If you prefer to not insert time-stamps when switching to the DONE
state (i.e. you have org-log-done at its default value of nil),
you can explicitly insert the EXPORT_DATE property too using the
below Org Capture template instead.
;; Populates both EXPORT_FILE_NAME and EXPORT_DATE properties in the inserted
;; headline.
(with-eval-after-load 'org-capture
(defun org-hugo-new-subtree-post-capture-template ()
"Returns `org-capture' template string for new Hugo post.
See `org-capture-templates' for more information."
(let* (;; http://www.holgerschurig.de/en/emacs-blog-from-org-to-hugo/
(date (format-time-string (org-time-stamp-format :long :inactive) (org-current-time)))
(title (read-from-minibuffer "Post Title: ")) ;Prompt to enter the post title
(fname (org-hugo-slug title)))
(mapconcat #'identity
`(
,(concat "* TODO " title)
":PROPERTIES:"
,(concat ":EXPORT_FILE_NAME: " fname)
,(concat ":EXPORT_DATE: " date) ;Enter current date and time
":END:"
"%?\n") ;Place the cursor here finally
"\n")))
(add-to-list 'org-capture-templates
'("h" ;`org-capture' binding + h
"Hugo post"
entry
;; It is assumed that below file is present in `org-directory'
;; and that it has a "Blog Ideas" heading. It can even be a
;; symlink pointing to the actual location of all-posts.org!
(file+olp "all-posts.org" "Blog Ideas")
(function org-hugo-new-subtree-post-capture-template))))
Images in Content
Conventionally any static content for a Hugo site, like images, PDF
files, and other attachments are put in the site static/
directory. Files in that directory are served at the site root when
the Hugo publishes that site. So all the content in there can be
accessed using the root prefix /. So a static/foo.png file can be
accessed at /foo.png.
More detail for this conventional approach can be found in the documentation for {{{doc(image-links#references-to-files-in-the-static-directory,referencing files in static directory)}}}.
But.. what about putting images along with the content?
OK, so with that short intro out of the way, ox-hugo supports
putting the attachment files in the same directory as the source Org
files!
In the below example, the left hand side shows the Org content and
attachment file organization that the user would need to do
manually. And the right hand side shows the structure that ox-hugo
will generate in the content/ and static/ (the conventional way
discussed above) directories.
./content-org ├── images-in-content/ │ ├── images-in-content.org │ │ * Post 1 (Org subtree) → ./content/images-in-content/post1.md │ │ * Post 2 (Org subtree) → ./content/images-in-content/post2.md │ ├── gnu.png → ./static/ox-hugo/gnu.png │ └── org.png → ./static/ox-hugo/org.png ├── post3/ │ ├── post3.org → ./content/images-in-content/post3.md │ └── gnu-copy.png → ./static/ox-hugo/gnu-copy.png └── post4/ ├── post4.org → ./content/images-in-content/post4.md └── org-copy.png → ./static/ox-hugo/org-copy.png
Have a look at the
content-org/images-in-content/images-in-content.org">content-org/images-in-content/images-in-content.org file for an
example of how to reference same-directory files (gnu.png and
org.png in this example) in an Org file (Hint: Just as you would do
in a normal Org file).
The benefit of this same-directory-attachment approach is that you
don't need to use any directory prefix to reference those files
(example: [[file:gnu.png]]). When ox-hugo exports those posts though,
the images are auto-copied to the static/ox-hugo/ directory2
and /ox-hugo/ prefix is added to those file references in the
exported Markdown files.
Also as the image file references are valid within the Org file, you
can see those images inline too!

Examples
Whether you prefer the subtree-based approach or file-based approach
for writing your posts in Org, there are examples for both in the
ox-hugo test suite:
Meta
Meta Features
- Extensive tests! – test/site/content-org (
make test) - Travis CI Integration – Checks with emacs versions starting from
24.4->25.2, with the latest stable version of
org-plus-contrib. - Documentation site – Generated using
ox-hugoitself (make doc_md hugo_doc). - GitHub repo files:
README.organdCONTRIBUTING.org– Generated usingox-org(make doc_gh). - Test site – Regenerated after each commit to this repo (
make test). - Passes
make testand generates documentation usingmake docin Termux app on Android too. - Passes
checkdocandpackage-lint(almost).
Contributing Guide
Debug
Test
Footnotes
COMMENT Local Variables ARCHIVE
For only subtree-based exports, you can set that special tag as
Org style tags too. Example: * I don't want to export this post
:no_no_dont_export:.. and don't forget to add that tag to
org-export-exclude-tags too!
To understand why the attachment files get copied to the
static/ox-hugo/ directory, have a look at the documentation for
{{{doc(image-links#references-to-files-outside-the-static-directory,referencing
files outside static directory)}}}.

