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.

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/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/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.

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/markdown-text.tsx
import {
  normalizeMathDelimiters,
  escapeCurrencyDollars,
} from "@assistant-ui/react-markdown";

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

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.