Something similar to this: emacs-org mode and html publishing: how to change structure of generated HTML

I want to have more control on the layout of HTML files generated from org-mode files. The default layout is good, but not good enough for my needs.

How can I change the default layout for any custom one?

2 Answers 2


A derived export backend is what you want. Here's an example where I've created a derived backend that changes the main html template.

In org-export-define-derived-backend you provide an alist with the parts of the backend you want to override.

In my backend I only wanted to replace the top level template.

(org-export-define-derived-backend 'pd-html 'html
     :translate-alist '((template . pd-html-template)))

pd-html-template is a function that takes the sub-elements and produces the final html. I'm just using basic string formatting and concatenation functions to produce the html. The following is a simplified implementation

(defun pd-html-template (contents info)
      "<!DOCTYPE html>\n"
      (format "<html lang=\"%s\">\n" (plist-get info :language))
      (format "<meta charset=\"%s\">\n"
       (coding-system-get org-html-coding-system 'mime-charset))
      (format "<title>%s</title>\n"
       (org-export-data (or (plist-get info :title) "") info))
      (format "<meta name=\"author\" content=\"%s\">\n"
       (org-export-data (plist-get info :author) info))
     "<link href=\"/css/style.css\" rel=\"stylesheet\" style=\"text/css\" />\n"
     (format "<h1 class=\"title\">%s</h1>\n"
       (org-export-data (or (plist-get info :title) "") info))

Other elements of the export backend can be customised in the same way; write a function that generates an output snippet and register in the translate-alist.

  • Instead of the nasty HTML interpolated format commands, Mustache.el is a cleaner way to go.
    – ocodo
    Commented Apr 21, 2015 at 1:37

Have a look at some of the HTML-derived exporter backends, for example https://github.com/yyr/org-mode/blob/master/contrib/lisp/ox-rss.el#L114.

This uses org-export-define-derived-backend to customize how the HTML backend processes org files:

(org-export-define-derived-backend 'rss 'html
 '(?r "Export to RSS"
       ((?R "As RSS buffer"
        (lambda (a s v b) (org-rss-export-as-rss a s v)))
    (?r "As RSS file" (lambda (a s v b) (org-rss-export-to-rss a s v)))
    (?o "As RSS file and open"
        (lambda (a s v b)
          (if a (org-rss-export-to-rss t s v)
        (org-open-file (org-rss-export-to-rss nil s v)))))))
  '((:description "DESCRIPTION" nil nil newline)
    (:keywords "KEYWORDS" nil nil space)
    (:with-toc nil nil nil) ;; Never include HTML's toc
    (:rss-extension "RSS_EXTENSION" nil org-rss-extension)
    (:rss-image-url "RSS_IMAGE_URL" nil org-rss-image-url)
    (:rss-categories nil nil org-rss-categories))
  :filters-alist '((:filter-final-output . org-rss-final-function))
  :translate-alist '((headline . org-rss-headline)
             (comment . (lambda (&rest args) ""))
             (comment-block . (lambda (&rest args) ""))
             (timestamp . (lambda (&rest args) ""))
             (plain-text . org-rss-plain-text)
             (section . org-rss-section)
             (template . org-rss-template)))

in this, example :filters-alist '((:filter-final-output . org-rss-final-function)) is used to specify a function org-rss-final-function that can exclude some items from being exported.

Then, :translate-alist '((headline . org-rss-headline) ... defines a bunch of other functions for certain elements, in this case headlines. These functions will be called with the org data structure of the element, and return the corresponding HTML string.

With this, you should be able to define your own HTML-derived org exporter that outputs exactly what you want.

  • May can you insert a simpler code example? My elisp isn't good enough to dig the code you use as example!
    – shackra
    Commented Apr 20, 2015 at 5:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.