How to add PrismJS syntax highlighting to HTML emails
Last updated: May 30, 2022
If you want to show a block of code in an HTML email and have it look nice, it usually involves a lot of manual work: escaping, formatting, tokenizing, styling tokens...
With Maizzle however, we can use JavaScript libraries to do that work for us 💅
Getting started
Let's create a new Maizzle project.
Open a terminal window and clone the Starter:
npx degit maizzle/maizzle example-syntax-highlight
Install dependencies:
cd example-syntax-highlight
npm install
Once it finishes installing dependencies open it in your editor.
We'll be covering two different techniques:
- with PostHTML
- with Markdown
For both techniques we'll be using the PrismJS library to highlight code blocks.
PostHTML
Using a PostHTML plugin, we can write our own <pre><code>
markup and have the plugin highlight the contents of the <code>
element.
Install plugin
First, let's install the posthtml-prism plugin, which we'll use to highlight code blocks:
npm i posthtml-prism
Next, add it to the plugins list in your config.js
:
module.exports = {
build: {
posthtml: {
plugins: [
require('posthtml-prism')()
]
}
}
}
Add code block
Add a block of code in your template, like so:
<pre>
<code class="language-javascript">
function foo(bar) {
var a = 42,
b = 'Prism';
return a + bar(b);
}
</code>
</pre>
language-javascript
class on the
<code>
tag - this is required in order to get language-specific syntax highlighting.
<pre>
tag yourself - see the
PostHTML example
in the tutorial repository.
Build
Run npm run dev
to start the development server, open http://localhost:3000/
in a browser, and navigate to the template.
You'll see something like this:
function foo(bar) {
var a = 42,
b = 'Prism';
return a + bar(b);
}
If you view the source of the page, you'll notice a lot of <span>
tags. This means it worked, and PrismJS has tokenized our code block.
But it's not very pretty, is it? We need a theme!
Theming
Choose one of the default themes, or see prism-themes for more.
For this tutorial, we'll go with a Tailwind adaptation the Synthwave '84 Theme.
Save prism-synthwave84.css to the src/css
directory in your project,
and import it into your src/css/tailwind.css
:
/* Tailwind CSS components */
@import "tailwindcss/components";
/**
* @import here any custom CSS components - that is, CSS that
* you'd want loaded before the Tailwind utilities, so the
* utilities can still override them.
*/
@import "custom/prism-synthwave84";
/* Tailwind CSS utility classes */
@import "tailwindcss/utilities";
/* Your custom utility classes */
@import "utilities";
Now, running npm run build
will yield the result we expected:
function foo(bar) {
var a = 42,
b = 'Prism';
return a + bar(b);
}
Markdown
Alternatively, we can also use Markdown to write fenced code blocks and have PrismJS automatically syntax-highlight them.
Install PrismJS
First, we must install the PrismJS library:
npm i prismjs
Configure Markdown
Next, we need to configure Maizzle to use PrismJS as a custom highlight function for the Markdown renderer.
We do that in config.js
:
const Prism = require('prismjs')
module.exports = {
markdown: {
markdownit: {
highlight(code, lang) {
lang = lang || 'markup'
return Prism.highlight(code, Prism.languages[lang], lang)
}
}
}
}
Fenced code block
We can now write code inside a fenced code block in our Template:
<extends src="src/layouts/main.html">
<block name="template">
<md>
```js
function foo(bar) {
var a = 42,
b = 'Prism';
return a + bar(b);
}
```
</md>
</block>
</extends>
Compatibility
Some email clients require extra steps in order to render our code blocks properly.
Gmail
Gmail will change our inline white-space: pre;
to white-space: pre-wrap;
.
This results in code wrapping, instead of showing a horizontal scrollbar.
Fix it by adding the following CSS at the beginning of prism-synthwave84.css
:
pre {
@apply whitespace-pre;
}
Outlook
Padding on <pre>
doesn't work in Outlook.
We can fix this by wrapping <pre>
inside a table that we only show in Outlook. We then style this table inline, like so:
<!--[if mso]>
<table style="width:100%;">
<tr>
<td style="background: #2a2139; padding: 24px;">
<![endif]-->
<pre>
<code class="language-javascript">
function foo(bar) {
var a = 42,
b = 'Prism';
return a + bar(b);
}
</code>
</pre>
<!--[if mso]></td></tr></table><![endif]-->
Production build
We've been developing locally so far, configuring PostHTML or Markdown in config.js
. This means CSS isn't inlined, and most email optimizations are off.
When you're satisfied with the dev preview, run npm run build
and use the template inside the build_production/
directory for sending the email.
Resources
- GitHub repository for this tutorial
- posthtml-prism plugin
- PrismJS library
- Synthwave '84 theme