Embedding LaTeX in MDX
Developer's Guide to Technical and Scientific Typography in MDX
LaTeX is the de facto markup language for the expression and communication of technical and scientific ideas. It is available as free software. Markdown is a lightweight markup language used to format text. It allows you to write using plain text syntax and convert it to structurally valid HTML.
The goal is straightforward: produce beautifully rendered technical material on your site.
Actually producing that is much less straightforward - creating reluctant developers and technical writers alike.
This article explores one approach to mathematical content delivery at scale.
Introduction
LaTeX is a high-quality typesetting system; it includes features designed for the production of technical and scientific documentation. LaTeX is the de facto standard for the communication and publication of scientific documents. LaTeX is available as free software. Markdown is a lightweight markup language used to format text. It allows you to write using plain text syntax and convert it to structurally valid HTML. I
A bespoke Unified plugin presented the most viable API for users to integrate and extend. Despite shortcomings from a debuggabity standpoint, the plugin behaves trivially. when spooning out mathematical content on the web. A bespoke Unified plugin presented the most viable API for users to integrate and extend. Despite shortcomings from a debuggabity standpoint, the plugin behaves trivially.
This article reviews the challenges involved in hosting technical and scientific content over the web. After translating problems into requirements, the article compares trade-off characterizing alternative technology stacks and shares guidance on intrgrating KaTeX and the unified ecosystem for serving technical and scientific content in Next.js applications.
NOTE
This article was updated in January 2026 to reflect the release of React 19.2 and Next.js 16, which introduce new features and improvements and also a few (notable) breaking changes. Make sure to follow the official docs for the latest updates and best practices.
Problem
This article explores problems encountered at scale in the delivery of mathematical content. Its motivation comes from:
-
Reduced Gatekeeping:
The democratization of technical content creation is gaining traction as more people seek to share knowledge online. Static websites PDFs and are no longer sufficient for interactive learning experiences. -
Few sites render lots of mathematics.
This means fewer battle-tested examples, less community knowledge, and tooling that often feels like an afterthought. -
Undifferentiated Heavy-lifting:
Technical writers rely on tools like Overleaf to produce ASTs, which are limited to PDFs. Non-programmers create shit ASTs; web developers create shit proofs. -
Prefetching causes overhead to grow with page count.
Modern frameworks like Next.js aggressively pre-fetch linked pages and eagerly loading aggressively pre-fetched mathematics produces significant rendering overhead. -
MDX doesn't support math by default.
MDX follows the CommonMark specification, which has no syntax for mathematical notation.
It takes inspiration from previous work, particularly:
- PreTeXt
- Overleaf
- Mathematics Stack Exchange
- Thomas W. Judson. Abstract Algebra: Theory and Applications
- The Encyclopedia of Mathematics
Requirements / Scope
This article covers the design of a software stack for technical writing at scale. It addresses the sociotechnical challenges involved.
combines pre-fetching, static-site generation, and above-the-fold code splitting.
It does not cover client-side redering techniques - use cases like
- user-inputted equations or live editors
- creating plugin from scratch
Essentially, if your math rendering solution adds 350KB of fonts and stylesheets per page or you have 50 blog posts visible in your navigation, naive implementations can make the browser prefetch megabytes of CSS before the user clicks anything. This makes your site slower, not faster.
Pre-fetching versus Pre-rendering Mathematics
Pre-fetching1 content is the process of delivering content from pages reachable from the current page in non-blocking fashion before users actually navigate to them. Pre-rendering2 (or "static-site generation") is the process of translating content and source code into browser-ready, target content before uploading the content to the server.
KaTeX vs.

The Unified Pipeline
MDX processing happens through the unified ecosystem, a collection of tools for parsing and transforming content. Understanding this pipeline is essential for integrating math support.
The key insight: all of this happens at build time. When a visitor loads your page, they receive pre-rendered HTML. No client-side JavaScript is required to display the equations—only CSS to style them.
remark-math: Recognizing Math Syntax
The remark-math plugin extends the Markdown parser to recognize LaTeX delimiters:
| Syntax | Type | Rendered As |
|---|---|---|
$E = mc^2$ | Inline math | Flows within text |
$$\int_0^1 x^2 dx$$ | Display math | Centered block |
Here's how remark-math transforms the AST. Given this input:
The parser produces nodes like:
For display math:
Produces:
From Markdown to HTML
After remark-rehype transforms the tree, math nodes become HTML elements with special classes:
At this stage, the LaTeX source is still plain text. The rehype plugin (KaTeX or MathJax) performs the actual rendering.
The Input Problem: Which LaTeX?
LaTeX is not a single specification. It's a macro language built on TeX, extended by hundreds of packages over four decades. When someone says "I want to write LaTeX," they might mean:
- TeX math mode: Core operators, Greek letters, fractions, roots
- AMS-LaTeX: Extended environments like
align,cases,matrix - Custom macros:
\newcommand{\R}{\mathbb{R}}for convenience - Specialized packages:
physics,chemfig,tikz
Neither KaTeX nor MathJax supports all of LaTeX. They implement subsets—substantial subsets, but subsets nonetheless.
KaTeX's Supported Subset
KaTeX focuses on common mathematical notation. It supports:
KaTeX does not support:
For the complete list, see KaTeX's supported functions.
Practical Guidance
Pick a renderer and write to its subset. For this article, we recommend KaTeX for most use cases. If you need a command KaTeX doesn't support, you have three options:
- Rewrite using supported commands. Often possible for standard mathematics.
- Define a custom macro. KaTeX supports
\newcommandfor simple substitutions. - Switch to MathJax. If you genuinely need broader coverage.
Here's an example of defining custom macros in your KaTeX configuration:
Now you can write $x \in \R$ instead of $x \in \mathbb{R}$.
The Output Problem: HTML+CSS vs SVG vs MathML
The renderer must produce something the browser can display. There are three approaches, each with tradeoffs.
HTML+CSS (KaTeX Default)
KaTeX renders math as nested <span> elements with precise CSS positioning:
Pros:
- Text is selectable and searchable
- Scales with browser font size
- Relatively small output size
- Renders crisply at any zoom level
Cons:
- Requires external CSS stylesheet (~25KB minified)
- Requires web fonts (~200KB for full set)
- Complex DOM structure can affect performance with many equations
- Browser font rendering varies slightly across platforms
SVG (MathJax Default)
MathJax can render to inline SVG:
Pros:
- Pixel-perfect rendering across all browsers
- Self-contained (no external fonts required if embedded)
- Consistent appearance regardless of installed fonts
Cons:
- Text is not selectable
- Larger output size (each equation includes full path data)
- Accessibility requires additional work
- Harder to style with CSS
MathML (Native Browser)
MathML is a W3C standard for mathematical markup:
Pros:
- Semantic markup (the browser understands it's a fraction)
- Accessible to screen readers natively
- No JavaScript or CSS required
- Small output size
Cons:
- Rendering quality varies significantly across browsers
- Firefox has excellent support; Chrome/Safari less so
- Spacing and typography often inferior to KaTeX/MathJax
- Limited styling options
The Accessibility Angle
MathJax invests heavily in accessibility. Its output includes:
- Speech text generation for screen readers
- Braille output support
- Interactive exploration (users can navigate equation structure)
- Multiple output formats optimized for different assistive technologies
KaTeX's approach is simpler: it includes hidden MathML alongside the visual HTML, allowing screen readers to access the equation structure. This works well for most cases but lacks MathJax's interactive features.
If accessibility is a primary requirement, MathJax is the stronger choice.
The Tradeoff: Bundle Size vs Rendering Speed vs Features
KaTeX
Characteristics:
- Synchronous rendering—no page reflows
- No external dependencies
- Processes math as fast as the browser can execute JavaScript
- Limited to its supported command subset
KaTeX's rendering is synchronous by design. When you call katex.render(), it returns immediately with the result. This means no layout shift, no flash of unstyled content, no waiting for web fonts before rendering.
MathJax
Characteristics:
- Asynchronous rendering in browser (not relevant for build-time)
- Tree-shakeable in v3—import only what you need
- Broader LaTeX support including AMS packages
- Multiple output formats (CHTML, SVG, MathML)
- Superior accessibility tooling
MathJax v3 is substantially faster than v2, but KaTeX remains faster for pure rendering speed. For build-time processing, the difference is negligible—both complete in milliseconds per equation.
When Each Makes Sense
Choose KaTeX when:
- Performance is critical (many equations, mobile users)
- Your math stays within its supported subset
- You prefer HTML+CSS output
- You want the simplest possible setup
Choose MathJax when:
- You need comprehensive LaTeX support
- Accessibility is a primary requirement
- You prefer SVG output
- You need features like equation numbering, cross-references
For this guide, we recommend starting with KaTeX. If you encounter unsupported commands, evaluate whether to rewrite them or switch to MathJax.
Recommended Stack for Next.js + MDX
Here's the complete setup for Next.js App Router with MDX math support.
Dependencies
next.config.ts
Loading KaTeX CSS
You must include KaTeX's stylesheet for proper rendering. In your root layout:
Alternatively, link to the CDN in your HTML head:
mdx-components.tsx
For the App Router, you need an MDX components file at the project root:
Example MDX File
Using MathJax Instead
To switch to MathJax, change your dependencies and config:
MathJax generates its CSS inline, so no external stylesheet is required. However, you may want to configure the output format:
Rendered Output Comparison
Given the input $\frac{a^2 + b^2}{c}$, here's what each renderer produces:
KaTeX (HTML+CSS):
MathJax SVG:
MathJax CommonHTML:
Code Splitting and Performance
For sites with many pages containing math, naive CSS loading can cause performance issues. Here are strategies to mitigate this.
Conditional CSS Loading
Only load KaTeX CSS on pages that actually use math:
Then include it only in pages with math:
Preloading Critical Fonts
If you know a page has math, preload the fonts to avoid layout shift:
Inlining Critical CSS
For the fastest possible render, inline the subset of KaTeX CSS needed for above-the-fold equations:
What This Article Doesn't Cover
Runtime rendering. If users can input equations (comments, live editors, interactive tools), you need client-side rendering. This requires loading KaTeX or MathJax in the browser and calling their APIs on user input. The build-time pipeline described here won't help.
Deep customization. Both renderers support extensive configuration: custom macros, theming, output tweaks, error handling. We've shown the basics; consult the official documentation for advanced use cases.
The implementation walkthrough. This article explains concepts and tradeoffs. For a step-by-step tutorial with a working repository, see the companion GitHub project (coming soon). For a video walkthrough, subscribe to the YouTube channel.
Conclusion
Rendering mathematics in MDX requires explicit tooling. The unified ecosystem provides the foundation: remark-math recognizes LaTeX syntax, and rehype-katex or rehype-mathjax transforms it into browser-renderable output.
The core decisions are:
- Input syntax: Write to KaTeX's subset unless you need MathJax's broader coverage
- Output format: HTML+CSS (KaTeX) for selectability and performance, SVG (MathJax) for pixel-perfect consistency
- Rendering engine: KaTeX for simplicity and speed, MathJax for features and accessibility
Build-time processing eliminates client-side complexity for static content. Your readers receive pre-rendered HTML; they don't need JavaScript to see the equations. With proper code-splitting, you can serve mathematical content without penalizing pages that don't need it.
Start with KaTeX. If you hit its limitations—unsupported commands, insufficient accessibility features, need for SVG output—switch to MathJax. Both integrate seamlessly with the same remark-math plugin; only the rehype plugin changes.
This article is part of a series on building technical documentation with Next.js. Next up: a hands-on walkthrough implementing everything described here.
Footnotes
-
The effectiveness of pre-fetching is attributed to the process of storing and reading frequently accessed content from in-memory cache makes use of the locality-of-reference principle. Consequently, pre-fetching has added benefit of reducing load on servers and networks, preserving functionality during network outages, and reducing failure points during application usage. ↩
-
Pre-rendering technique contrasts with server-side rendering and client-side rendering in the following ways: Pre-rendering (SSG) produces content at build-time3 on the developer's machine or CI server before deployment, so the server serves fully-formed pages. Server-side rendering (SSR) produces content at request-time on the server when a user requests a page. Client-side rendering (CSR) produces content at load-time in the client's browser after the initial HTML shell is loaded. ↩
-
This article uses the terms build-time, request-time, and load-time to refer to distinct moments in the lifecycle of web content delivery. Build-time refers to when the developer compiles and deploys the site (before artifacts are uploaded to a web server). Request-time refers to when a web server establishes a TCP connection (immediately after consuming packets from a client). Load-time refers to when a browser fires the load event (after parsing all resources and completing the compositing stage). ↩