The Markdown parser in Ghost does not parse LaTeX\LaTeX, but you can parse it separately using other tools and post them on your Ghost blog.

In this tutorial, I'll show you how to use KaTeX\KaTeX with Ghost. KaTeX\KaTeX is a fast typesetting library that helps you print TeX\TeX on the web.

Set up

  1. Install Node.js
  2. Create a package.json with this content:
{
  "name": "compiler-for-ghost",
  "version": "1.0.0",
  "description": "Compile Markdown to HTML for Ghost",
  "main": "src/compiler.js",
  "dependencies": {
    "rehype-highlight": "^4.0.0",
    "rehype-katex": "^3.0.0",
    "rehype-raw": "^4.0.2",
    "rehype-react": "^6.0.0",
    "rehype-stringify": "^8.0.0",
    "remark-emoji": "^2.1.0",
    "remark-external-links": "^6.1.0",
    "remark-footnotes": "^1.0.0",
    "remark-math": "^2.0.1",
    "remark-parse": "^8.0.2",
    "remark-rehype": "^7.0.0",
    "remark-slug": "^6.0.0",
    "remark-toc": "^7.0.0",
    "unified": "^9.0.0"
  },
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
  1. Run sudo npm install -g npm-check-updates.
  2. Run ncu -u.
  3. Run npm install.

Create a Compiler

Create a compilers.js file with the following content:

'use strict';
const unified = require('unified');
const parse = require('remark-parse');
const remark2rehype = require('remark-rehype');

const math = require('remark-math');
const rehypeKatex = require('rehype-katex');
const highlight = require('rehype-highlight');
const emoji = require('remark-emoji');
const externalLinks = require('remark-external-links');
const toc = require('remark-toc');
const footnotes = require('remark-footnotes');
const slug = require('remark-slug');
const raw = require('rehype-raw');
const stringify = require('rehype-stringify');

const processor = unified()
  .use(parse, { commonmark: true })
  .use(slug)
  .use(toc, { maxDepth: 2 })
  .use(externalLinks, { rel: ['noopener'] })
  .use(footnotes, { inlineNotes: true })
  .use(remark2rehype, { allowDangerousHtml: true })
  .use(raw)
  .use(math)
  .use(rehypeKatex)
  .use(highlight, { ignoreMissing: true })
  .use(emoji)
  .use(stringify);

Command Line Approach

To run the compiler without specifying your .md and .html files within compiler.js, add this code to the bottom of compiler.js:

/**
 * stdin = './path/to/BlogPost.md';
 * stdout = './path/to/BlogPost.html';
 * node compiler.js < ./path/to/BlogPost.md > ./path/to/BlogPost.html
 */
process.stdin.pipe(stream(processor)).pipe(process.stdout);

To compile your Markdown to HTML, run this command:

node compiler.js < ./path/to/BlogPost.md > ./path/to/BlogPost.html

:::warning This approach will overwrite your ./path/to/BlogPost.html file if it already exists. :::

File System Approach

To specify the .md and .html files within compiler.js, add this code to the bottom of compiler.js:

const fs = require('fs');
const fileIn = './path/to/BlogPost.md';
const fileOut = './path/to/BlogPost.html';
const text = fs.readFileSync(fileIn, { encoding: 'utf-8' });
const html = processor.processSync(text);
fs.writeFileSync(fileOut, html, { encoding: 'utf-8' });

To compile your Markdown to HTML, run this command:

node compiler

:::warning This approach will overwrite your ./path/to/BlogPost.html file if it already exists. :::

Stylesheets

Finally, add the KaTeX\KaTeX stylesheets to your Ghost blog as a code injection to the header. You can add it globally if you need to use it often, or to the code injection for each individual post. You can find the latest links to the CDN on GitHub:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css" integrity="sha384-zB1R0rpPzHqg7Kpt0Aljp8JPLqbXI3bhnPWROx27a9N0Ll6ZP/+DiW/UqRcLbRjq" crossorigin="anonymous">

Examples

Further Resources

Give back

The techniques described in this tutorial depend heavily on free and open-source plugins developed by the Unifiedjs Collective. If you think their plugins are useful, please support their work on Open Collective.