Render Markdown HTML in Python with Jinja2

Dec 31, 2021

By now, we can use Jinja to render HTML templates in Python. You can check out that post here. Now, I want a way to write code blocks with syntax highlighting on my HTML pages. Let's turn to Markdown.

Markdown is a markup language which provides formatting to plain-text documents. It allows us to quickly create bullet lists, links, and even code blocks without writing complex HTML.

Jinja doesn't have native support for Markdown so we'll need to use a third-party library called Mistune.

pip install mistune

Let's return to our generate.py script which renders a static HTML page with Python. We'll add function for rendering markdown.

from mistune import html

MARKDOWN = """
# This is Markdown

* Hello World!
"""

def render_markdown(markdown):
return html(markdown)


class HtmlGenerator(object):
...
def generate(self):
...
with open(self._build_path('public/%s' % OUTPUT_FILE_NAME), 'w') as html_file:
html = template.render(
title='Sample Page',
content=render_markdown(MARKDOWN)
)

If all you want is basic formatting, then you're done! If you're looking for syntax highlighting for your code blocks, then stay tuned.

Mistune doesn't add the syntax highlighting by default. For that, we'll need another library called Pygments.

pip install pygments

Now let's add the syntax highlight renderer.

from jinja2 import Environment
from jinja2 import FileSystemLoader
-from mistune import html
+from mistune import escape
+from mistune import Markdown
+from mistune import HTMLRenderer
+from pygments import highlight
+from pygments.lexers import get_lexer_by_name
+from pygments.formatters import html
+from pygments.styles import get_style_by_name


+class SyntaxHighlightRenderer(HTMLRenderer):
+ def block_code(self, code, lang):
+ if not lang:
+ return '\n<pre><code>%s</code></pre>\n' % escape(code)
+ lexer = get_lexer_by_name(lang, stripall=True)
+ formatter = html.HtmlFormatter(lineseparator="<br>")
+ return highlight(code, lexer, formatter)
-def render_markdown(markdown):
- return html(markdown)

+def render_markdown(markdown_string):
+ renderer = SyntaxHighlightRenderer()
+ markdown = Markdown(renderer=renderer)
+ return markdown(markdown_string)

Now we just need to include the CSS rules in our HTML. We'll store them in the Jinja globals so we don't need to manually render them.

generate.py

MARKDOWN = """
# This is Markdown

* Hello World!
+
+```py
+print('Hello World!')
+```
+"""

class SyntaxHighlightRenderer(HTMLRenderer):
@@ -43,7 +47,8 @@ class HtmlGenerator(object):
def __init__(self, template_name):
self.template_name = template_name
self.env = Environment(loader=FileSystemLoader('template'))
+ formatter = html.HtmlFormatter()
+ self.env.globals['highlight_styles'] = formatter.get_style_defs()

template/sample_template.html

<html lang="en">
<head>
<title>{{ title }}</title>
+ <style>{{ highlight_styles }}</style>
</head>
<body>
<div>This is the header</div>

That's it! You can now render Markdown in your Python Jinja templates! Our resulting HTML looking something like this:


This is Markdown

print('Hello World!')