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

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="../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="../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

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"))))

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.

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.