LaTeX in Chat Messages

Render LaTeX math expressions in AI chat messages with KaTeX — drop-in equation support for React chat UIs built on assistant-ui.

Render LaTeX mathematical expressions in chat messages using KaTeX.

Warning

LaTeX rendering is not enabled by default.

Install dependencies

npm install katex rehype-katex remark-math

Add KaTeX CSS to your layout

/app/layout.tsx
import "katex/dist/katex.min.css";

Update markdown-text.tsx

/components/assistant-ui/elements/markdown-text.tsx
import { MarkdownTextPrimitive } from "@assistant-ui/react-markdown";
import remarkMath from "remark-math";
import rehypeKatex from "rehype-katex";

const MarkdownTextImpl = () => {
  return (
    <MarkdownTextPrimitive
      remarkPlugins={[remarkGfm, remarkMath]} // add remarkMath
      rehypePlugins={[rehypeKatex]}           // add rehypeKatex
      className="aui-md"
      components={defaultComponents}
    />
  );
};

export const MarkdownText = memo(MarkdownTextImpl);

Supported Formats

By default, remark-math (react-markdown path) supports:

  • $...$ for inline math
  • $$...$$ for display math
  • Fenced code blocks with the math language identifier

Supporting Alternative LaTeX Delimiters

Many language models emit math in delimiters that remark-math does not recognize:

  • \(...\) for inline math and \[...\] for display math
  • custom tags like [/math]...[/math] and [/inline]...[/inline]

@assistant-ui/react-markdown exports normalizeMathDelimiters, which rewrites these to the $...$ and $$...$$ form remark-math parses. Pass it to the preprocess prop of MarkdownTextPrimitive:

/components/assistant-ui/elements/markdown-text.tsx
import {
  MarkdownTextPrimitive,
  normalizeMathDelimiters,
} from "@assistant-ui/react-markdown";

const MarkdownTextImpl = () => {
  return (
    <MarkdownTextPrimitive
      remarkPlugins={[remarkGfm, remarkMath]}
      rehypePlugins={[rehypeKatex]}
      preprocess={normalizeMathDelimiters}
      className="aui-md"
      components={defaultComponents}
    />
  );
};

The individual transforms rewriteLatexBracketDelimiters and rewriteCustomMathTags are exported too, for finer control over which delimiters are normalized.

Info

Using Streamdown as your renderer? The same helpers are exported from @assistant-ui/react-streamdown and accepted by the preprocess prop of StreamdownTextPrimitive.

Currency amounts

With single-dollar inline math enabled (the default on the react-markdown path), remark-math reads a lone $ as a math delimiter, so prose such as $5 ... $10 is parsed as math. escapeCurrencyDollars escapes a $ immediately followed by a digit so currency survives, while leaving the $$ of display math intact. Compose it with the delimiter normalization:

/components/assistant-ui/elements/markdown-text.tsx
import {
  normalizeMathDelimiters,
  escapeCurrencyDollars,
} from "@assistant-ui/react-markdown";

<MarkdownTextPrimitive
  preprocess={(text) => escapeCurrencyDollars(normalizeMathDelimiters(text))}
  // ...
/>;

Tip

Inside MarkdownTextPrimitive, the streamed text first passes through preprocess (delimiter normalization) and then through useSmooth (character by character accumulation), and only then reaches the markdown parser. Both run before remark-math sees the text, so delimiter replacement and the streaming smoothing stay streaming safe; a partially received delimiter is accumulated in the smoothing buffer rather than parsed mid fragment.