Skip to main content

๐Ÿ“ฆ Self-host KaTeX assets in Docusaurus

To ensure your Docusaurus site can render mathematical expressions properly, you need to include the KaTeX CSS and font assets. This guide will walk you through the process of self-hosting these assets, which can improve performance and reliability by eliminating external dependencies.

1. Install Required Pluginsโ€‹

To render math equations in Docusaurus, you need to install the remark-math and rehype-katex plugins. These plugins allow you to write LaTeX-style math in your Markdown files, which will be rendered as beautiful mathematical expressions.

npm install --save remark-math rehype-katex
npm install --save-dev @types/remark-math @types/rehype-katex

2. Download KaTeX Assetsโ€‹

The Dynamic Download Script (scripts/download-katex.js) This script automates the process of downloading the latest KaTeX assets and placing them in the correct directory for self-hosting. It fetches the minified CSS and JavaScript files, as well as the necessary font files, ensuring that your Docusaurus site can render mathematical expressions properly without relying on external CDNs. To use the script, simply run:

scripts/download-katex.js
const fs = require("fs");
const path = require("path");
const https = require("https");

const LATEST_VERSION = "latest"; // You can specify a version like '0.16.8' if you want a specific one
const BASE_URL = `https://cdn.jsdelivr.net/npm/katex@${LATEST_VERSION}/dist/`;
const TARGET_DIR = path.join(__dirname, "..", "static", "katex");
const FONTS_DIR = path.join(TARGET_DIR, "fonts");

// Helper to fetch data as a string
const fetchText = (url) => {
return new Promise((resolve, reject) => {
https
.get(url, (res) => {
let data = "";
res.on("data", (chunk) => (data += chunk));
res.on("end", () => resolve(data));
})
.on("error", reject);
});
};

// Helper to download binary files
const downloadFile = (url, dest) => {
return new Promise((resolve, reject) => {
const file = fs.createWriteStream(dest);
https
.get(url, (res) => {
if (res.statusCode !== 200) return reject(`Status: ${res.statusCode}`);
res.pipe(file);
file.on("finish", () => {
file.close();
resolve();
});
})
.on("error", (err) => {
if (fs.existsSync(dest)) fs.unlinkSync(dest);
reject(err);
});
});
};

async function main() {
try {
console.log(`๐Ÿ” Fetching KaTeX ${LATEST_VERSION} manifest...`);

if (!fs.existsSync(FONTS_DIR)) fs.mkdirSync(FONTS_DIR, { recursive: true });

// 1. Download and save the CSS
const cssUrl = `${BASE_URL}katex.min.css`;
const cssContent = await fetchText(cssUrl);
fs.writeFileSync(path.join(TARGET_DIR, "katex.min.css"), cssContent);
console.log("โœ… katex.min.css saved.");

// 2. Parse CSS for font filenames
// This regex looks for fonts/filename.woff2 (or .woff, .ttf)
const fontRegex = /fonts\/([\w-]+\.(woff2|woff|ttf))/g;
const matches = [...cssContent.matchAll(fontRegex)];
const uniqueFonts = [...new Set(matches.map((m) => m[1]))];

console.log(
`found ${uniqueFonts.length} unique fonts. Starting download...`,
);

// 3. Download each font
const downloads = uniqueFonts.map(async (fontName) => {
try {
await downloadFile(
`${BASE_URL}fonts/${fontName}`,
path.join(FONTS_DIR, fontName),
);
console.log(` โœ… ${fontName}`);
} catch (err) {
console.error(` โŒ Failed: ${fontName} (${err})`);
}
});

await Promise.all(downloads);
console.log("\nโœจ All assets synchronized successfully.");
} catch (error) {
console.error("๐Ÿš€ Fatal Error:", error);
process.exit(1);
}
}

main();

How to Run

  1. Save the above code in a file named download-katex.js inside the scripts directory of your Docusaurus project.
  2. Open a terminal and navigate to your Docusaurus project root.
  3. Run the script using Node.js:
node scripts/download-katex.js

3. Configure Docusaurusโ€‹

Then, in your docusaurus.config.ts, make sure you are importing the plugins correctly. Note that in newer Docusaurus versions, you often need to use import at the top rather than require.

docusaurus.config.ts
// docusaurus.config.ts
import remarkMath from "remark-math";
import rehypeKatex from "rehype-katex";

const config: Config = {
// ...
presets: [
[
"classic",
{
docs: {
remarkPlugins: [remarkMath],
rehypePlugins: [rehypeKatex],
},
},
],
],
stylesheets: [
{
href: "/katex/katex.min.css",
type: "text/css",
},
],
};

Force the KaTeX CSS to load by importing it directly in your src/css/custom.css (even though you are self-hosting):

src/css/custom.css
/* Importing the KaTeX CSS file to ensure proper styling of mathematical expressions. */
@import url("/katex/katex.min.css");

KaTeX Asset Test

If your self-hosting setup is working correctly, the equations below should look professional and aligned. If they look like raw text or "exploded" symbols, the CSS or Fonts are missing.

1. Inline Mathโ€‹

This test verifies that KaTeX is processing text within a paragraph. The equation E=mc2E = mc^2 should be inline.

Subscript Test: The variables x1,x2,โ€ฆ,xnx_1, x_2, \dots, x_n should have clear subscripts. (If you see italics but no positioning, the CSS is missing).

2. Display Block (The Big Stuff)โ€‹

This tests the $$ block syntax and multi-line alignment.

I=โˆซ02ฯ€sinโก(x)โ€‰dx=0I = \int_{0}^{2\pi} \sin(x) \, dx = 0

3. Font & Symbol Stress Testโ€‹

This verifies that your /static/katex/fonts/ directory is correctly linked. If you see "boxes" instead of symbols, the fonts didn't download or are in the wrong path.

Matrix Test:

(abcd)ร—(1001)=(abcd)\begin{pmatrix} a & b \\ c & d \end{pmatrix} \times \begin{pmatrix} 1 & 0 \\ 0 & 1 \end{pmatrix} = \begin{pmatrix} a & b \\ c & d \end{pmatrix}

Greek & Accents: ฮฑ,ฮฒ,ฮณ,ฮ“,ฯ€,ฯ•,ฯƒ,ฮถ\alpha, \beta, \gamma, \Gamma, \pi, \phi, \sigma, \zeta. x^,yห‰,z~\hat{x}, \bar{y}, \tilde{z}.

4. MDX Component Interopโ€‹

Since this is an .mdx file, let's ensure a standard React component doesn't break the math nearby.

info

The definition of the derivative is limโกhโ†’0f(x+h)โˆ’f(x)h\lim_{h \to 0} \frac{f(x+h) - f(x)}{h}.


Troubleshooting the "Broken" Lookโ€‹

SymptomProbable CauseFix
Raw symbols (โˆซ\int)Plugin missingCheck remark-math and rehype-katex in docusaurus.config.ts.
Symbols are stacked/overlappingCSS missingCheck path in stylesheets or try @import in custom.css.
Missing symbols (empty boxes)Fonts missingEnsure /static/katex/fonts/ is populated with .woff2 files.
MDX Error: "Expected X"MDX Brace ConflictWrap math in $ or use \{ to escape curly braces if using TS variables.

An emoji to represent your KaTeX or Math configurationโ€‹

  1. For the Configuration Section

    ๐Ÿ“ (Triangle Ruler): The universal symbol for geometry and math config.

    ๐Ÿงฎ (Abacus): Great for a "Calculations" or "Math Engine" category.

    โš™๏ธ (Gear): The standard "Settings" icon, often paired with math symbols like โš™๏ธ+โˆ‘.

  2. For the Assets / Self-Hosting Folder

    ๐Ÿ“ฆ (Package): Represents your /static/katex bundle.

    ๐Ÿ”ก (Input Latin Uppercase): Good for representing the fonts folder.

    ๐ŸŽจ (Palette): If you are specifically talking about the KaTeX CSS/Styles.

  3. For the Download Script

    ๐Ÿ“ฅ (Download Tray): Perfect for download-katex.js.

    ๐Ÿค– (Robot): For the automation aspect of the script.

    โšก (Thunderbolt): Represents a fast "pre-build" setup step.

  4. For your Sidebar / Docs

    ฯ€ (Pi): If your theme supports Unicode, this is the most direct.

    โ™พ๏ธ (Infinity): Often used for advanced math documentation.

    ๐Ÿ“ (Memo): Standard for math notes or theorem pages.

website/
โ”œโ”€โ”€ ๐Ÿ“‚ scripts/
โ”‚ โ””โ”€โ”€ ๐Ÿ“ฅ download-katex.js
โ”œโ”€โ”€ ๐Ÿ“‚ static/
โ”‚ โ””โ”€โ”€ ๐Ÿ“‚ katex/ ๐Ÿ“
โ”‚ โ”œโ”€โ”€ ๐Ÿ“„ katex.min.css
โ”‚ โ””โ”€โ”€ ๐Ÿ“‚ fonts/
โ”œโ”€โ”€ โš™๏ธ docusaurus.config.ts
โ””โ”€โ”€ ๐Ÿ“‚ docs/
โ””โ”€โ”€ ๐Ÿงฎ math-guide.mdx