The Markdown preprocessor renders math by extracting it with hand-written regexes (markKatex in kit/preprocessors/mdsvex/index.js) before Markdown runs, then rendering the stashed TeX with katex.renderToString. There is a concrete bug in that extraction, plus a broader issue: the custom delimiter rules diverge from standard KaTeX in ways that silently bite contributors.
1. Bug: inline \(...\) eats the whitespace before it
const REGEX_LATEX_INLINE = /\s\\\\\(([\s\S]+?)\\\\\)/g;
// ...
.replace(REGEX_LATEX_INLINE, (_, tex) => {
const marker = `KATEXPARSE${counter++}MARKER`;
markedKatex[marker] = { tex, displayMode: false };
return marker; // the leading \s that was matched is never restored
});
The regex matches a leading \s, but the replacement returns only marker, so that whitespace character is consumed and dropped. Two visible effects:
- A space between text and an inline formula disappears:
... one step \(S_{t+1}\) renders as one stepS_{t+1} (glued).
- Standalone equations run together: when the eaten character is the newline separating two
\(...\) lines, the blank-line paragraph break collapses and consecutive equations jam onto one line.
The $...$ branch a few lines below handles this correctly — it captures the surrounding whitespace and re-emits it (return spaceBefore + marker + spaceAfter). The \( branch (and the $$ branch, which eats a leading \n) do not.
Fix — assert the boundary without consuming it, or capture-and-restore:
const REGEX_LATEX_INLINE = /(?<=\s)\\\\\(([\s\S]+?)\\\\\)/g; // lookbehind
// or: /(\s)\\\\\(([\s\S]+?)\\\\\)/g -> return spaceBefore + marker;
2. The delimiter rules are inconsistent
| Syntax |
Rule in markKatex |
\(...\) inline |
requires (and eats) one leading whitespace |
$...$ inline |
requires whitespace on both sides |
$$...$$ display |
requires (and eats) a leading newline |
\[...\] display |
not handled at all |
There is no consistent mental model, and the natural display counterpart of \(...\) — \[...\] — is not supported (you must use $$).
3. The real cost: standard KaTeX syntax silently fails
Contributors will reach for standard KaTeX syntax, but much of it does not render here — it silently becomes literal text instead of math. Examples that fail:
$x$ right before punctuation or markup, e.g. $x$. or **$x$**
\[ ... \] display math — not handled at all
\(...\) at the start of a line — no leading space for the regex to match
We hit this while fixing one page in the Deep RL course: using standard $...$ and \[...\] silently broke ~20 formulas, and it took reading this file to find out why.
Suggestion: handle math with the standard toolchain — remark-math + rehype-katex, or KaTeX's auto-render — instead of hand-written regexes, so standard syntax just works and the issues above go away together.
Where it shows
Live example on the Deep RL course: https://huggingface.co/learn/deep-rl-course/unit2/mc-vs-td (source units/en/unit2/mc-vs-td.mdx). Before/after below:

The Markdown preprocessor renders math by extracting it with hand-written regexes (
markKatexinkit/preprocessors/mdsvex/index.js) before Markdown runs, then rendering the stashed TeX withkatex.renderToString. There is a concrete bug in that extraction, plus a broader issue: the custom delimiter rules diverge from standard KaTeX in ways that silently bite contributors.1. Bug: inline
\(...\)eats the whitespace before itThe regex matches a leading
\s, but the replacement returns onlymarker, so that whitespace character is consumed and dropped. Two visible effects:... one step \(S_{t+1}\)renders asone stepS_{t+1}(glued).\(...\)lines, the blank-line paragraph break collapses and consecutive equations jam onto one line.The
$...$branch a few lines below handles this correctly — it captures the surrounding whitespace and re-emits it (return spaceBefore + marker + spaceAfter). The\(branch (and the$$branch, which eats a leading\n) do not.Fix — assert the boundary without consuming it, or capture-and-restore:
2. The delimiter rules are inconsistent
markKatex\(...\)inline$...$inline$$...$$display\[...\]displayThere is no consistent mental model, and the natural display counterpart of
\(...\)—\[...\]— is not supported (you must use$$).3. The real cost: standard KaTeX syntax silently fails
Contributors will reach for standard KaTeX syntax, but much of it does not render here — it silently becomes literal text instead of math. Examples that fail:
$x$right before punctuation or markup, e.g.$x$.or**$x$**\[ ... \]display math — not handled at all\(...\)at the start of a line — no leading space for the regex to matchWe hit this while fixing one page in the Deep RL course: using standard
$...$and\[...\]silently broke ~20 formulas, and it took reading this file to find out why.Suggestion: handle math with the standard toolchain —
remark-math+rehype-katex, or KaTeX'sauto-render— instead of hand-written regexes, so standard syntax just works and the issues above go away together.Where it shows
Live example on the Deep RL course: https://huggingface.co/learn/deep-rl-course/unit2/mc-vs-td (source
units/en/unit2/mc-vs-td.mdx). Before/after below: