How to blog with Emacs Org mode

The org-publish tool allows you to publish your website or blog with Emacs Org mode.
118 readers like this.
Typewriter in the grass

Original photo by jetheriot. Modified by Rikki Endsley. CC BY-SA 2.0.

I used WordPress for the first few years of my blog, but I really wanted to publish it entirely using GNU Emacs. I tried Org2Blog, but something was still missing and it felt unsatisfying. I tried to create a website to publish Emacs configs, which I named Haqiba (an unusual name, I know), first using Django and then Jekyll. Jekyll is cool and provides more control over content and publishing, but I still couldn't blog directly from Emacs, and Org mode was still missing. Although I tried adding Org mode support to Jekyll with jekyll-org, the framework seemed alien.

I finally found the solution I was looking for when I started using org-publish. I had stumbled upon org-publish earlier in my search, but at first, I thought it was too complex for blogging. But I gave it a try and have been happy ever since.

A lot of websites, including the ones on this list, use org-publish. For example, Bernt Hansen's Org mode—Organize your life in plain text not only uses org-publish to publish content but also offers a lot of information to give you a deeper understanding about Org mode.

Advantages of org-publish

Among its features, org-publish offers:

  • Good control of configurations, CSS, media, and publishing
  • Org mode formatting support
  • Static file generation
  • Easy deployment using GitLab and GitHub CI/CD
  • Easy hosting via Apache/Nginx/file-server if you prefer copying files to a remote server instead of using GitLab Pages or GitHub Pages
  • Version control
  • Everything in GNU Emacs. Yay!

Basic setup

The Org-publish tutorial provides a basic template to get you started. I encourage you to go through the tutorial, as the basic setup in this tutorial is just enough to give you a brief understanding of org-publish. Start by configuring a variable called org-publish-project-alist in a publish.el file inside your myblog/ project directory. Place the following content in publish.el:

(require 'ox-publish)

(setq org-publish-project-alist
      '(("posts"
         :base-directory "posts/"
         :base-extension "org"
         :publishing-directory "public/"
         :recursive t
         :publishing-function org-html-publish-to-html
         :auto-sitemap t)
        ("all" :components ("posts"))))

The first line is an import statement. The variable org-publish-project-alist has a list of publishing projects to control publishing behavior. The first element, posts, is where all of the configurations specific to blog posts are done. For example, the property :base-directory configures the directory where all the posts (in Org format) are saved. Similarly, :publishing-directory configures the directory to save generated HTML files from Org files. Setting the :recursive property to t will recursively generate HTML from all the Org files within posts/ and its subdirectories. The :auto-sitemap property generates sitemap.html with your list of posts (you will tweak this below). Finally, :publishing-function org-html-publish-to-html converts all of the org files to HTML. While you can also define your own functions, for the purposes of this demo, use the built-in function provided by ox-publish.

You need a few posts for testing, so create a file named posts/post_one.org and include some basic headers with some content. Use C-c C-e # default and C-c C-e # html to include default and HTML templates, respectively.

Your file should look something like this:

#+title: Post One
#+date: <2020-02-12 Wed>
#+author: John Doe
#+email: john.doe@example.com

Lorem Ipsum is simply dummy text of the printing and typesetting industry.

The setup is almost done. You can use M-x org-publish-all to generate the HTML and use make to handle publishing. Following is the content of the Makefile:

# Makefile for myblog

.PHONY: all publish publish_no_init

all: publish

publish: publish.el
	@echo "Publishing... with current Emacs configurations."
	emacs --batch --load publish.el --funcall org-publish-all

publish_no_init: publish.el
	@echo "Publishing... with --no-init."
	emacs --batch --no-init --load publish.el --funcall org-publish-all

clean:
	@echo "Cleaning up.."
	@rm -rvf *.elc
	@rm -rvf public
	@rm -rvf ~/.org-timestamps/*

Here is the current layout of the project:

myblog
├── Makefile
├── posts
│   └── post_one.org
└── publish.el

Executing make will generate sitemap.html and post_one.html in the public/ directory:

myblog
├── Makefile
├── posts
│   ├── post_one.org
│   └── sitemap.org
├── public
│   ├── post_one.html
│   └── sitemap.html
└── publish.el

Webpage published with org-publish

Add CSS to your post

You can enhance the publish.el file to include elements like CSS or images. To try this out, add a section or project for CSS. The modified publish.el should look like this:

(require 'ox-publish)

(setq org-publish-project-alist
      '(("posts"
          :base-directory "posts/"
          :base-extension "org"
          :publishing-directory "public/"
          :recursive t
          :publishing-function org-html-publish-to-html
          :auto-sitemap t)
         ("css"
          :base-directory "css/"
          :base-extension "css"
          :publishing-directory "public/css"
          :publishing-function org-publish-attachment
          :recursive t)
         ("all" :components ("posts" "css"))))

Create a new directory named css/ and copy the code from site.css into it. Now, create a second post to test the CSS.

#+title: Post Two
#+date: <2020-02-12 Wed>
#+author: John Doe
#+email: john.doe@example.com
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="https://opensource.com/../css/site.css" />

Lorem Ipsum is simply dummy text of the printing and typesetting industry.

In this example, the CSS is included using the #+HTML_HEAD: option. The org-publish tutorial recommends using the #+STYLE: option to include the stylesheet, but this did not work for me. Instead, I used #+HTML_HEAD:, as CSS support in the Org mode manual suggests.

Here is the layout that displays the css/ directory:

myblog
├── css
│   └── site.css
├── Makefile
├── posts
│   ├── post_one.org
│   └── post_two.org
└── publish.el

Having to include #+HTML_HEAD: in every post will soon become tedious. There are also multiple stylesheets in a website. To solve this issue, use the #+SETUPFILE: option:

#+title: Post Two
#+date: <2020-02-12 Wed>
#+author: John Doe
#+email: john.doe@example.com
#+SETUPFILE: ../org-template/style.org

Lorem Ipsum is simply dummy text of the printing and typesetting industry.

The org-template/style.org file includes the path to the stylesheet:

#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="https://opensource.com/../css/site.css" />

Following is the final layout:

myblog
├── css
│   └── site.css
├── Makefile
├── org-template
│   └── style.org
├── posts
│   ├── post_one.org
│   └── post_two.org
└── publish.el

Webpage published with org-publish

Tweak the sitemap

The final configuration will generate an index.html file instead of a sitemap.html file. Rename the title and configure the author and email across the website. Below is the finished publish.el file:

(require 'ox-publish)

(setq org-publish-project-alist
      '(("posts"
         :base-directory "posts/"
         :base-extension "org"
         :publishing-directory "public/"
         :recursive t
         :publishing-function org-html-publish-to-html
         :auto-sitemap t
         :sitemap-title "Blog Index"
         :sitemap-filename "index.org"
         :sitemap-style list
         :author "John Doe"
         :email "john.doe@example.com"
         :with-creator t)
        ("css"
         :base-directory "css/"
         :base-extension "css"
         :publishing-directory "public/css"
         :publishing-function org-publish-attachment
         :recursive t)
	 ("all" :components ("posts" "css"))))

Webpage index published with org-publish

If you are having difficulty setting up the project, you can view the entire project on my GitLab page.

Use an existing org-publish setup

It can become tedious to create blogs with org-publish from scratch. To make it easier, you can use my repository as a base template to publish your own blogs using org-publish.

To use it, clone the blog_template branch:

git clone https://gitlab.com/psachin/psachin.gitlab.io -b blog_template --single-branch myblog

Use make to export Org pages to HTML. The public/ directory will have all the files required for hosting:

cd myblog
make

There is a sample blog post in posts/template.org for reference. You can use the .gitlab-ci.yaml file to publish the content of public/ as a GitLab Page.

Website homepage published with org-publish

Website About page published with org-publish

Bonus tip 1

After executing the make command, the public/ directory will have all the files necessary for hosting a static site. All you have to do is to configure the webserver to serve this directory, or you can render the blog locally using Python's built-in http.server module.

With Python 3.6, use:

cd myblog/public
python -m http.server

If you have Python 3.7, you can serve public/ using:

cd myblog
python -m http.server --directory=public

Open http://localhost:8000/ in your web browser to view your website.

Bonus tip 2

This is my favorite tip. If an idea for a new blog post pops into my mind when I don't have time to work on it, I quickly create a draft using an Org capture template. I use the template definition below to open a buffer window by typing C-c c p. When I'm finished, I type C-c C-c to save the draft.

Copy this Elisp snippet into your existing Emacs configuration file (but make to sure the change the file path):

(defun create-blog-post ()
	"Create an org file in ~/source/myblog/posts."
	(interactive)
	(let ((name (read-string "Filename: ")))
	(expand-file-name (format "%s.org" name) "~/source/myblog/posts/")))

(setq org-capture-templates
	'(("p" "Post" plain
		(file create-blog-post)
		(file "~/.emacs.d/org-templates/post.orgcaptmpl"))))

Here are the contents of ~/.emacs.d/org-templates/post.orgcaptmpl:

#+title: %^{Name}
#+date: <%<%Y-%m-%d>>
#+keywords: draft
#+setupfile: ../org-templates/post.org

%?

#+INCLUDE: "../disquss.inc"

For a more thorough explanation of the Org capture template, you can watch my video demonstration.

Have you used Org mode to publish a website or blog, or do you plan to? Let us know your experience in the comments.

What to read next

Who cares about Emacs?

GNU Emacs has been around for a long time—since 1983—but its continuous development makes it still relevant today.

psachin
Sachin is passionate about Free and Open source software. He is avid GNU Emacs user and likes to talk and write about open source, GNU/Linux, Git, and Python. He has previously worked on OpenStack, ManageIQ/CloudForms & Red Hat Insights. He also likes to explore Swift Object Storage in his spare time. He can be reached on IRC as psachin@{Libera.Chat, Freenode, OFTC, gnome}.

Comments are closed.

Creative Commons LicenseThis work is licensed under a Creative Commons Attribution-Share Alike 4.0 International License.