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.

Step 1a: Set up the after-save-hook #

  1. 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 * Footnotes header 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.

  2. Then save the file, and do revert-buffer.

  3. You will be prompted to add that eval line to your Customize setup, hit ! to permanently save that setting and prevent future prompts.

Step 1b: Prevent auto-export during Org Capture #

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 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.

Step 1: Set up the 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 (< (let ((case-fold-search t))
                   (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!

Fork me on GitHub