org-mode html publishing reference
Introduction
This post documents various org-mode snippets and processes for producing the html you see on this screen.
Why org-mode?
org-mode is a great way to organize text documents in emacs.
The editor makes interacting with the elements and organizing them very simple.
The text output is also easily consumable and parseable.
It’s just text, but its annotated in such a way that interacting with it programmatically becomes fairly trivial.
org elements
org documents are made up of “elements”.
Major elements
- Headings
Headings are used to define structures and sections.
All of the headings in this document are annotated this way.
* Level 1 ** Level 2 *** Level 3
The more *’s you add the further your hierarchy will nest.
- Lists
Lists can be ordered
1. One 2. Two 3. Three
Unordered
– One – Two – Three
- Tables
Tables are just data structured with pipes
| Column A | Column B | Column C | | row 1 a | row 2 a | row 3 c | | row 2 a | row 2 a | row 2 c |
When exporting to html they get rendered like regular
<table>elements
Column A Column B Column C row 1 a row 2 a row 3 c row 2 a row 2 a row 2 c - Blocks
- Keywords & Properties
- Timestamps
- TODO items
Objects
The Setup
The basic structure is simple:
mel.rocks/ ├── org/ # Source files │ ├── posts/ # Blog posts │ └── pages/ # Static pages ├── public/ # Generated HTML └── publish.el # Configuration
Writing Posts
Creating a new post is as simple as:
#<buffer 2025-12-10-my-new-post.org>
Then add the metadata:
TITLE: My New Post AUTHOR: Your Name DATE: 2025-12-10 Tue Your content goes here...
Publishing Workflow
The entire publishing process is handled by a single command:
make build # Generate HTML make serve # Preview locally make deploy # Push to Cloudflare
Code Examples
Org-mode handles code blocks beautifully. Here’s a Python example:
def hello_world(): """A simple greeting function.""" return "Hello from org-mode!" print(hello_world())
And some JavaScript:
const greet = (name) => { return `Hello, ${name}!`; }; console.log(greet("World"));
Tables
org-mode tables are cool. you define them as plain text like so
| Feature | Markdown | Org-Mode | |-------------+----------+-----------| | Tables | Limited | Excellent | | Code Blocks | Good | Excellent | | Linking | Basic | Advanced | | Export | HTML | Multiple |
Seems simple enough but when you interact with them in emacs it balances everything for you and allows for excel like functionality.
When you input the same table as part of the doc the publishing tool renders it like so as html:
| Feature | Markdown | Org-Mode |
|---|---|---|
| Tables | Limited | Excellent |
| Code Blocks | Good | Excellent |
| Linking | Basic | Advanced |
| Export | HTML | Multiple |
Images and Media
Files
Adding images is straightforward:
[[file:../img/screenshot.png]]
You can also add captions and attributes:
#+CAPTION: My awesome screenshot #+ATTR_HTML: :width 600px :alt Screenshot description [[file:../img/screenshot.png]]
URLs
I added a special rule where instead of specifying file: in the tag you specify img: to treat a URL as an image tag.
#+CAPTION: A keyboard, I think. #+ATTR_HTML: :width 600px [[img:https://upload.internetz.club/cxd6nToc]]
Deployment to Cloudflare Pages
Cloudflare Pages makes hosting simple and free:
- Push your code to GitHub
- Connect Cloudflare Pages to your repository
- Set build command:
emacs --batch -q --load publish.el - Set output directory:
public
Every push to main triggers a new deployment automatically.
Tips and Tricks
Use Snippets
Create yasnippet templates for common post formats:
# -*- mode: snippet -*- # name: blogpost # key: blog # -- #+TITLE: ${1:Title} #+AUTHOR: Mel Gray #+DATE: `(format-time-string "<%Y-%m-%d %a>")` #+DESCRIPTION: ${2:Description} #+KEYWORDS: ${3:keywords} $0
Preview While Writing
Use org-preview-html-mode or run a local server:
make serve & # Run in background make watch # Auto-rebuild on changes
Organize with Tags
Use tags for categorization:
**My Post Title : emacs : programming : tutorial :
Conclusion
This setup gives me everything I need:
- Simple writing experience in Emacs
- Version control with Git
- Complete control over the output