Server-side KaTeX rendering with Jekyll
This post explains how I set up server-side KaTeX rendering to improve the load time on pages that include mathematical notation.
KaTeX is a math typesetting library that lets you render nice-looking math
equations like inside of an HTML document. As the KaTeX
documentation describes, the easiest way to
use KaTeX is to import their CSS and JavaScript from your website’s <head>
element by adding these three lines:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"
onload="renderMathInElement(document.body);"></script>
With this client-side setup, visitors to your site only receive the KaTeX
“source” (something like $$f(x) = e^{rt}$$
) from your server, and then their
computer runs the code (KaTeX) that transforms this into MathML and tells the
browser how to style and arrange the individual symbols.
However, by running KaTeX on the server instead of the client, you can send readers the MathML code directly, which removes the need for redundant computations and queries to the KaTeX CDN. Guillaume Endignoux and Xuning Yang explain how to achieve this setup using Jekyll, the static site generator I use. In summary:
- Add the
duktape
,execjs
,katex
, andkramdown-math-katex
gems to yourGemfile
. (You can leave outduktape
if you already have Node.js on your path.) -
Set the following options in
_config.yml
:markdown: "kramdown" kramdown: math_engine: "katex"
-
Add the following line to your site’s
<head>
element:<link rel="stylesheet" href="/assets/katex.css">
- Download a KaTeX release from
GitHub and extract
katex.css
andfonts/
to your source repo’sassets/
directory.
The last step left me unsatisfied. As I described in a previous post, I want to avoid introducing a
third-party code dependency to my source tree—especially one like the KaTeX CSS
that I’d have to keep manually in sync with the version of KaTeX installed by
the Ruby katex
gem. Instead, I tweaked my configure.sh
script to
automatically retrieve the KaTeX fonts and CSS from within the katex
gem after
installing it with Bundler:
function install_katex_resources () {
KATEX_CSS_SRC="$(find ./vendor/ -ipath "*/vendor/katex/stylesheets/katex.css")"
KATEX_CSS_DEST="./assets/katex.css"
rm "$KATEX_CSS_DEST" 2> /dev/null
if [ ! -f "$KATEX_CSS_SRC" ]
then
echo "E: Failed to locate source katex.css" >&2
return 1
fi
ln -v "$KATEX_CSS_SRC" "$KATEX_CSS_DEST"
KATEX_FONTS_SRC=$(find ./vendor/ -ipath "*/vendor/katex/fonts/*.woff2")
KATEX_FONTS_DEST="./assets/fonts"
rm "$KATEX_FONTS_DEST"/*.woff2 2> /dev/null
# shellcheck disable=SC2068 # splitting intended
for f in ${KATEX_FONTS_SRC[@]}
do
BASENAME=$(basename "$f")
ln -v "$f" "$KATEX_FONTS_DEST"/"$BASENAME"
done
}
This puts the files in the assets/
directory—right where they need to be—but
keeps Bundler in control of their versions. Using a hard link (ln a b
) instead
of copying (cp a b
) the files spares a few unnecessary disk operations when
rerunning configure.sh
.
I admit that the maintenance burden this eliminates isn’t large, but in a project with many dependencies, the little things add up.