v0.2.0 — component output without allowHtml, React MathBlock, protectMath, KaTeX error badges
Summary
New opt-in component output mode for block math that works without allowHtml, a React subpath with a ready-made MathBlock component, math protection helpers, styled KaTeX error reporting, several parsing improvements, wider @tanstack/markdown peer range, and a streaming bug fix.
Changes
New output mode & React
output: 'component'for block math —mathBlockExtension({ output: 'component' })emits aComponentNode(name: 'math', raw TeX inproperties.tex) instead of a pre-renderedhtmlnode. The extension also registers arenderHtmlhook, so the HTML renderer renders KaTeX with zero config and noallowHtml.tagNameoption (default'MathBlock') — custom element/component name for the emitted node.- React subpath —
import { MathBlock } from 'tanstack-markdown-math/react'renderstexvia KaTeX (displayModedefaults totrue). Map it with<Markdown components={{ MathBlock }}>to render block math withoutallowHtml.
Math protection helpers (new exports)
protectMath(content)— replaces_and*inside$...$/$$...$$spans with private-use Unicode placeholders (MATH_UNDERSCORE_SUB,MATH_ASTERISK_SUB) before markdown parsing, preventing the parser from turning TeX subscripts ($H_2O$) into emphasis.restoreMathChars(tex)— restores the original characters. Called automatically by the extensions before rendering; exported for custom renderers and tests.
Error reporting
- Styled KaTeX errors — invalid TeX now renders a styled inline badge / display box (Tailwind classes,
katex-errormarker preserved) showing the error title and the offending TeX, instead of KaTeX's raw red fallback. Detection covers bothclass="katex-error"and the#cc0000legacy output. Atry/catcharoundrenderToStringguards against throwing renderers.
Parsing improvements
- Inline
$$...$$— display-style delimiters used inline (e.g.text $$\rightarrow$$ text) are now rendered as inline math via the inline extension. $$x$$ trailing textno longer swallows the page — a line like$$\rightarrow$$ Identification des mécanismesis treated as a paragraph with inline math, not an unclosed block fence.- Math inside
inlineHtml— math inside inline HTML elements (e.g.<u>$V_1$</u>) is now rendered whenallowHtmlis on. - Bug fix (streaming) — an unclosed single-line
$$\frac{1}{no longer drops the TeX after the opening$$; the partial content is rendered as before.
Packaging
- Peer range widened —
@tanstack/markdownis now>=0.0.13 <0.1.0(was^0.0.13, which excluded0.0.14).
Impact on existing users
No action required for basic usage. The public API is additive only (output, tagName, protectMath, restoreMathChars are optional/new). However, three behaviors are new by default — review if your content matches:
- Invalid TeX output changed — previously KaTeX's raw red error HTML; now a styled badge/box (still contains
katex-errorfor detection). Customrenderoptions are unaffected. - Inline
$$...$$is now math — documents using literal doubled dollars inline will now render math there. Escape as\$if you need literal text. - Math inside inline HTML is now transformed — with
allowHtml: true,$...$inside inline HTML elements renders as math (previously left verbatim).
allowHtml: true is still required for the default html/inlineHtml output — in particular for inline math (see known limitation below).
Optional: block math without allowHtml
import { Markdown } from '@tanstack/markdown/react'
import { mathExtension, MathBlock } from 'tanstack-markdown-math/react'
// or: import { mathExtension } from 'tanstack-markdown-math'
<Markdown
extensions={mathExtension({ output: 'component' })}
components={{ MathBlock }}
>
{source}
</Markdown>Optional: underscore-heavy documents
import { parseMarkdown } from '@tanstack/markdown'
import { mathExtension, protectMath } from 'tanstack-markdown-math'
const doc = parseMarkdown(protectMath(source), {
extensions: mathExtension(),
})Known limitation
Inline math ($...$) still requires allowHtml: true. First-class inline component nodes are tracked upstream in TanStack/markdown#9; this package will adopt them when available.