Suppose you'd like to compose HTML emails1 in Emacs' message-mode.2 An HTML email is really just a MIME message with two parts: a text/plain part and its text/html alternative. Creating a MIME message [by hand] is boring and non-trivial. Therefore, [Emacs includes a] library called mml[…] that parses a language called MML and generates MIME messages. You can read the gory mml details in (emacs-mime) Composing. For now, though, consider this sample document:

<#multipart type=alternative>
hello, world
<#part type=text/html>
    <title>HTML version of email</title>
    <p>hello, world</p>

OK, this looks doable, but for one big, glaring problem: you have to write, by hand, both the text and HTML versions of the email. Holy redundant redundancy, Batman!

Enter Markdown. It's a text-to-HTML conversion tool [that] allows you to write using an easy-to-read, easy-to-write plain text format[.] Check it (emphasis mine):

The overriding design goal for Markdown’s formatting syntax is to make it as readable as possible. The idea is that a Markdown-formatted document should be publishable as-is, as plain text, without looking like it’s been marked up with tags or formatting instructions. While Markdown’s syntax has been influenced by several existing text-to-HTML filters, the single biggest source of inspiration for Markdown’s syntax is the format of plain text email.

If you're used to composing plain text emails, your existing writing habits are probably pretty close to Markdown, so it shouldn't be too hard to learn. So what we'd like to do is to simply use Markdown syntax for the text part, and then use a Markdown→HTML processor to generate the HTML section for us.

The first thing I did was to quickly hack up a command-line tool, mimedown, which takes Markdown-formatted text on stdin, and spits out mml containing both the text and HTML versions on stdout. I used the Python markdown library, which you can download from SourceForge.

#!/usr/bin/env python
# mimedown - generate multipart text and HTML MIME message from Markdown input.

import markdown

def mimedown(input, output):
    text =
    html = markdown.Markdown(text, safe_mode=False).toString()
    html = "<htm><head><title></title><body>%s</body></html>" % html
    print >> output, '''<#multipart type=alternative>
<#part type=text/html>
''' % (text, html)

if __name__ == '__main__':
    import sys
    mimedown(sys.stdin, sys.stdout)

The next step is to hook this into message-mode somehow. Here's a mimedown command which runs the message body through mimedown:

(defun mimedown ()
    (shell-command-on-region (point) (point-max) "mimedown" nil t)))

There you have it. You can now compose HTML emails in Emacs with Markdown. Share and Enjoy!


  1. Before you get angry, know that generally, I'm against the use of HTML email.
  2. Emacs ships with two mail composition modes, mail-mode and message-mode. I read mail with Gnus, which uses message-mode by default. Also, message-mode's support for MIME composition is much, much better than that of mail-mode.


  1. Nice trick, out of curiosity, why did you use the python version, and not the reference (perl) implementation? If nothing else, the perl one worked out of the box (no wrapper script to write).

    I'll be adding this to my Gnus setup for sure.

    Trey Jackson, 4 January 2008

  2. Um, duh (in answer to my question), because either way you need the wrapper to generate the multi-part portion... sigh.

    Trey Jackson, 4 January 2008

  3. Well, the mml could be generated elisp-side, so in principle you could use whatever markdown implementation you want.

    That being said, I already had the python markdown library installed (I use it on this blog), so it seemed the obvious choice.

    Edward O'Connor, 4 January 2008

  4. Ok, last spam comment, just had a followup post:

    Trey Jackson, 4 January 2008

  5. Hey hober, there is also muse-message, if you haven't seen that. It takes a plain text e-mail message, which you might be writing in Gnus, and turns it into a dual-format text/html MIME message.

    John Wiegley, 6 January 2008

Add a comment