Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
If you’ve ever tried to write mathematics in a web app, you know the pain of clumsy plaintext: sqrt(x^2 + y^2), 1/2mv^2, or E=mc^2 crammed together without proper symbols. It’s readable in a pinch, but it looks amateurish and quickly becomes impossible for complex equations. With MathJax v4, you can render professional, publication-quality math directly in the browser—complete with fractions, matrices, vectors, and equation numbering—using the same LaTeX syntax mathematicians and scientists rely on every day. This tutorial walks you through setting up MathJax in Next.js and building simple React components so your math looks like math, not like ASCII art.
Create a new Next.js project:
npx create-next-app@latest mathjax-nextjsYou’ll be asked several configuration questions. Here are the recommended answers:
After the project is created, navigate to the project directory:
cd mathjax-nextjsHere is what your project’s directory should like like:
mathjax-nextjs/
|--src/
|----app/
|------globals.css
|------layout.tsx
|------page.tsx
|--public/
|--package.json
|--tsconfig.json
|--next.config.ts
|--README.md
Install the project dependencies:
npm install
npm run devVisit http://localhost:3000 to confirm the app is running.
You’re project directory structure should look the same as before:
mathjax-nextjs/
|--src/
|----app/
|------globals.css
|------layout.tsx
|------page.tsx
|--public/
|--package.json
|--tsconfig.json
|--next.config.ts
|--README.md
What we’ll accomplish: Configure Next.js to properly handle MathJas’x font files so mathematical symbols render correctly.
Why this step is needed: MathJax uses special web fonts to display mathematical symbols. Without this configuration, the fonts might not load properly, causing math symbols to appear as boxes or missing characters.
Add a webpack configuration option to handle MathJax font files in the Next.js configuration file at mathjax-nextjs/next.config.ts:
import type {NextConfig} from 'next';
const nextConfig: NextConfig = {
webpack: (config) => {
// Handle MathJax static assets
config.module.rules.push({
test: /\.woff2$/,
type: 'asset/resource'
});
return config;
},
};
export default nextConfig;What is Webpack? Webpack is a module bundler that Next.js uses to combine and optimize your code, styles, and assets. It handles different file types and prepares them for the browser.
Why is this needed for MathJax? MathJax uses web fonts (.woff2 files) to display mathematical symbols. This webpack configuration ensures these font files are properly handled and loaded by the browser, preventing missing or broken math symbols in your rendered equations.
Your project should have the same structure as before:
mathjax-nextjs/
|--src/
|----app/
|------globals.css
|------layout.tsx
|------page.tsx
|--public/
|--package.json
|--tsconfig.json
|--next.config.ts (updated with the webpack configuration)
|--README.md
What we’ll accomplish: Load MathJax from CDN and configure it to recognize LaTeX delimiters ($...$ for inline math, $$...$$ for display math) and support various LaTeX features.
Why this step is needed: MathJax needs to be loaded globally so it can scan the entire page for mathematical expressions. The configuration tells MathJax which delimiters to look for and how to process them.
Update src/app/layout.tsx to include the needed MathJax configuration:
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'
const inter = Inter({ subsets: ['latin'] })
export const metadata: Metadata = {
title: 'MathJax v4 + Next.js',
description: 'MathJax v4 integration with Next.js',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<head>
{/* MathJax v4 Configuration */}
<script
dangerouslySetInnerHTML={{
__html: `
MathJax = {
// LaTeX processing configuration
tex: {
inlineMath: [['$', '$']],
displayMath: [['$$', '$$']],
processEscapes: true,
tags: 'ams',
tagSide: 'right',
tagIndent: '0.2em'
},
// Output rendering configuration
output: {
font: 'mathjax-newcm'
},
// Initialization and startup configuration
startup: {
pageReady() {
return MathJax.startup.defaultPageReady();
}
}
};
`,
}}
/>
{/* MathJax v4 CDN */}
<script defer src="https://cdn.jsdelivr.net/npm/mathjax@4/tex-chtml.js"></script>
</head>
<body className={inter.className}>{children}</body>
</html>
)
}
Key Configuration Options:
inlineMath: [['$', '$']] – Defines delimiters for inline math expressionsdisplayMath: [['$$', '$$']] – Defines delimiters for display math expressionsprocessEscapes: true – Processes LaTeX escape sequences like \\ to \ (needed for backslashes in LaTeX commands)tags: 'ams' – Enables automatic equation numberingtagSide: 'right' – Places equation numbers on the right sidetagIndent: '0.2em' – Controls spacing between equations and their numbersWhy dangerouslySetInnerHTML is needed: MathJax needs to process raw HTML strings that contain LaTeX delimiters. The dangerouslySetInnerHTML prop allows us to inject the MathJax configuration script directly into the HTML head, bypassing React’s JSX parsing. This is necessary because MathJax must be configured before the page loads, and the configuration contains JavaScript that needs to be executed as-is.
Why defer is used in the MathJax CDN script: The defer attribute ensures that the MathJax script downloads in parallel with HTML parsing but executes only after the HTML document has been fully parsed. This prevents the script from blocking page rendering while ensuring MathJax is available when the page content loads.
Your project directory should still look the same:
mathjax-nextjs/
|--src/
|----app/
|------globals.css
|------layout.tsx (updated with the needed MathJax configuration)
|------page.tsx
|--public/
|--package.json
|--tsconfig.json
|--next.config.ts (updated with the webpack configuration)
|--README.md
What we’ll accomplish: Define TypeScript type declarations for the global MathJax object so TypeScript understands MathJax’s API and can catch errors.
Why this step is needed: TypeScript needs to know about the MathJax object that gets added to the global window. Without these type declarations, TypeScript will show errors when we try to use window.MathJax.
Create a new file types/mathjax.d.ts in the project root folder (next to package.json) for TypeScript support. This file contains type declarations, not configuration values:
declare global {
interface Window {
MathJax: {
tex: {
inlineMath: string[][] // Delimiters for inline math (e.g., [['$', '$']])
displayMath: string[][] // Delimiters for display math (e.g., [['$$', '$$']])
processEscapes: boolean // Whether to process LaTeX escape sequences
processEnvironments?: boolean // Whether to process LaTeX environments
tags?: string // Equation numbering style ('ams' for automatic numbering)
tagSide?: string // Side for equation labels ('left' or 'right')
tagIndent?: string // Indentation for equation labels
}
output: {
font: string // Math font to use (e.g., 'mathjax-newcm')
scale?: number // Overall scaling factor for math
minScale?: number // Minimum scale factor
maxScale?: number // Maximum scale factor
}
startup: {
pageReady: () => Promise<void> // Function called when page is ready
defaultPageReady: () => Promise<void> // Default page ready function
}
typesetPromise: (elements?: (Element | null)[]) => Promise<void> // Function to render math elements
loader?: {
paths: { font: string } // Paths for font loading
load: string[] // Extensions to load
}
}
}
}
export {}
Here is what you’re project directory should look like after adding this new file:
mathjax-nextjs/
|--src/
|----app/
|------globals.css
|------layout.tsx (updated with the needed MathJax configuration)
|------page.tsx
|--types/
|----mathjax.d.ts (new file)
|--public/
|--package.json
|--tsconfig.json
|--next.config.ts (updated with the webpack configuration)
|--README.md
What we’ll accomplish: Create a simple test page to verify that MathJax is working correctly before building custom components.
Why this step is needed: We need to confirm that MathJax is properly loaded and can render mathematical expressions. This helps us identify any configuration issues early.
Update src/app/page.tsx to test basic MathJax functionality:
import React from 'react'
export default function Home() {
return (
<main>
<h1>MathJax v4 + Next.js Test</h1>
<div>
<h2>Inline Math Test</h2>
<p>
Einstein's equation: $E = mc^2$
</p>
<p>
Pythagorean theorem: $a^2 + b^2 = c^2$
</p>
<p>
Quadratic formula: {`$x = \\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a}$`}
</p>
</div>
<div>
<h2>Display Math Test</h2>
<div>
$$E = mc^2$$
</div>
<div>
{`$$\\sum_{i=1}^{n} i = \\frac{n(n+1)}{2}$$`}
</div>
<div>
{`$$\\int_{-\\infty}^{\\infty} e^{-x^2} dx = \\sqrt{\\pi}$$`}
</div>
</div>
<div>
<h2>Aligned Equations</h2>
<div>
{`$$\\begin{align} (a + b)^2 &= a^2 + 2ab + b^2 \\\\ &= a^2 + b^2 + 2ab \\\\ &= (a + b)(a + b) \\end{align}$$`}
</div>
<div>
{`$$\\begin{align*} x &= 2y + 3 \\\\ &= 2(y + 1.5) \\\\ &= 2z \\quad \\text{where } z = y + 1.5 \\end{align*}$$`}
</div>
</div>
<div>
<h2>Matrices and Arrays</h2>
<div>
{`$$\\begin{pmatrix} a & b \\\\ c & d \\end{pmatrix}$$`}
</div>
<div>
{`$$\\begin{bmatrix} 1 & 2 & 3 \\\\ 4 & 5 & 6 \\\\ 7 & 8 & 9 \\end{bmatrix}$$`}
</div>
<div>
{`$$\\begin{vmatrix} a & b \\\\ c & d \\end{vmatrix} = ad - bc$$`}
</div>
<div>
{`$$\\begin{array}{|c|c|c|} \\hline x & y & x + y \\\\ \\hline 1 & 2 & 3 \\\\ 4 & 5 & 9 \\\\ 7 & 8 & 15 \\\\ \\hline \\end{array}$$`}
</div>
</div>
<div>
<h2>Vectors</h2>
<div>
{`$$\\vec{v} = \\begin{pmatrix} x \\\\ y \\\\ z \\end{pmatrix}$$`}
</div>
<div>
{`$$\\mathbf{v} = v_x\\hat{i} + v_y\\hat{j} + v_z\\hat{k}$$`}
</div>
<div>
{`$$\\|\\vec{v}\\| = \\sqrt{v_x^2 + v_y^2 + v_z^2}$$`}
</div>
</div>
<div>
<h2>Equation Labels and References</h2>
<div>
{`$$\\begin{equation} E = mc^2 \\label{eq:einstein} \\end{equation}$$`}
</div>
<div>
{`$$\\begin{equation} F = ma \\label{eq:newton} \\end{equation}$$`}
</div>
<div>
{`$$\\begin{equation} \\sum_{i=1}^{n} i = \\frac{n(n+1)}{2} \\label{eq:sum} \\end{equation}$$`}
</div>
<p>Referencing equations: Einstein's equation ({`\\ref{eq:einstein}`}) and Newton's law ({`\\ref{eq:newton}`}).</p>
</div>
<div>
<h2>Advanced Examples</h2>
<div>
{`$$\\begin{align} \\nabla \\cdot \\mathbf{E} &= \\frac{\\rho}{\\epsilon_0} \\label{eq:gauss} \\\\ \\nabla \\cdot \\mathbf{B} &= 0 \\label{eq:gauss-mag} \\\\ \\nabla \\times \\mathbf{E} &= -\\frac{\\partial \\mathbf{B}}{\\partial t} \\label{eq:faraday} \\\\ \\nabla \\times \\mathbf{B} &= \\mu_0\\mathbf{J} + \\mu_0\\epsilon_0\\frac{\\partial \\mathbf{E}}{\\partial t} \\label{eq:ampere} \\end{align}$$`}
</div>
<p>Maxwell's equations: {`\\ref{eq:gauss}`}, {`\\ref{eq:gauss-mag}`}, {`\\ref{eq:faraday}`}, and {`\\ref{eq:ampere}`}.</p>
</div>
</main>
)
}
Now test the setup:
http://localhost:3000$E = mc^2$), MathJax isn’t workingNote: a hard browser refresh may be needed to see the changes.
When writing LaTeX expressions in JSX, you’ll notice two different syntax approaches in the examples above:
<p>Einstein's equation: $E = mc^2$</p>
<div>$$E = mc^2$$</div><div>{`$$\\sum_{i=1}^{n} i = \\frac{n(n+1)}{2}$$`}</div>Why the difference:
E = mc^2 can be written directly in JSX without special handling\frac, \sum, \sqrt, \begin, etc.) need template literals ({``}) because JSX treats backslashes as escape charactersTemplate literals allow for multi-line formatting, a common practice when writing LaTeX code for readability:
<DisplayMath>{`
\\begin{pmatrix}
a & b \\\\
c & d
\\end{pmatrix}
`}</DisplayMath>
This makes complex LaTeX expressions much more readable and organized, especially for matrices, arrays, and multi-line equations.
Rule of thumb: If your LaTeX contains backslashes, wrap it in template literals:
{`your LaTeX here`}Your project directory should now look like this:
mathjax-nextjs/
|--src/
|----app/
|------globals.css
|------layout.tsx (updated with the needed MathJax configuration)
|------page.tsx (updated with examples to test rendering)
|--types/
|----mathjax.d.ts (new file)
|--public/
|--package.json
|--tsconfig.json
|--next.config.ts (updated with the webpack configuration)
|--README.md
At this point, you have a working MathJax v4 integration with Next.js! The setup above will render any LaTeX expressions you write directly in your JSX using $...$ for inline math and $$...$$ for display math. This approach works perfectly for simple use cases and is great for testing.
While the basic integration works well, creating custom React components for math rendering provides significant benefits:
<InlineMath> and <DisplayMath> make your code more readableLet’s create these components to enhance your math rendering capabilities.
What we’ll accomplish: Create a directory to organize our custom MathJax components.
Why this step is needed: We’ll be creating multiple React components for math rendering, so we need a dedicated directory to keep them organized and separate from pages.
Create a directory to organize our custom MathJax components:
mkdir src/app/componentsNow you’re project should look like this:
mathjax-nextjs/
|--src/
|----app/
|------components/ (new directory to store our custom components)
|------globals.css
|------layout.tsx (updated with the needed MathJax configuration)
|------page.tsx (updated with examples to test rendering)
|--types/
|----mathjax.d.ts (new file)
|--public/
|--package.json
|--tsconfig.json
|--next.config.ts (updated with the webpack configuration)
|--README.md
What we’ll accomplish: Create a React component that renders inline mathematical expressions (math that appears within text).
Why this step is needed: We need a reusable component that can take LaTeX as input and render it as inline math. The component uses dangerouslySetInnerHTML to inject the LaTeX with $...$ delimiters so MathJax can process it.
Create src/app/components/InlineMath.tsx:
'use client'
// React hooks for managing component lifecycle and DOM references
import { useEffect, useRef } from 'react'
// TypeScript interface defining the component's props
interface InlineMathProps {
children: string
className?: string
}
const InlineMath = ({ children, className = '' }: InlineMathProps) => {
// Reference to the DOM element that will contain the math
const mathRef = useRef<HTMLSpanElement>(null)
// Effect hook to handle MathJax typesetting when component mounts or children change
useEffect(() => {
const typesetMath = () => {
// Check if MathJax is loaded and ready to typeset
if (mathRef.current && (window as any).MathJax && (window as any).MathJax.typesetPromise) {
(window as any).MathJax.typesetPromise([mathRef.current])
.catch((err: any) => {
if (typeof console !== 'undefined' && console.error) {
console.error('InlineMath typesetting error:', err)
}
})
// If MathJax is loaded but typesetPromise isn't ready yet, retry after a short delay
} else if (mathRef.current && (window as any).MathJax) {
setTimeout(typesetMath, 50)
}
}
// Check if we're in a browser environment and MathJax is already loaded
if (typeof window !== 'undefined' && (window as any).MathJax) {
typesetMath()
} else {
// If MathJax isn't loaded yet, poll for it with exponential backoff
const checkMathJax = () => {
if ((window as any).MathJax && (window as any).MathJax.typesetPromise) {
typesetMath()
} else {
setTimeout(checkMathJax, 100)
}
}
// Start polling for MathJax to load
setTimeout(checkMathJax, 100)
}
}, [children])
return (
// Span element that will contain the LaTeX and be processed by MathJax
<span
ref={mathRef}
className={`inline-math ${className}`}
dangerouslySetInnerHTML={{ __html: `$${children}$` }}
/>
)
}
export default InlineMathYour project directory should now look like this:
mathjax-nextjs/
|--src/
|----app/
|------components/ (new directory to store our custom components)
|--------InlineMath.tsx (new file)
|------globals.css
|------layout.tsx (updated with the needed MathJax configuration)
|------page.tsx (updated with examples to test rendering)
|--types/
|----mathjax.d.ts (new file)
|--public/
|--package.json
|--tsconfig.json
|--next.config.ts (updated with the webpack configuration)
|--README.md
What we’ll accomplish: Create a React component that renders display mathematical expressions (math that appears on its own line, centered) with optimized width for better equation label positioning.
Why this step is needed: Display math needs different formatting than inline math. This component renders LaTeX with $$...$$ delimiters and uses 50% width to naturally bring equation labels closer to the equations.
Create src/app/components/DisplayMath.tsx:
'use client'
// React hooks for managing component lifecycle and DOM references
import { useEffect, useRef } from 'react'
// TypeScript interface defining the component's props
interface DisplayMathProps {
children: string
className?: string
centered?: boolean
}
const DisplayMath = ({ children, className = '', centered = true }: DisplayMathProps) => {
// Reference to the DOM element that will contain the math
const mathRef = useRef<HTMLDivElement>(null)
// Effect hook to handle MathJax typesetting when component mounts or children change
useEffect(() => {
const typesetMath = () => {
// Check if MathJax is loaded and ready to typeset
if (mathRef.current && (window as any).MathJax && (window as any).MathJax.typesetPromise) {
(window as any).MathJax.typesetPromise([mathRef.current])
.catch((err: any) => {
if (typeof console !== 'undefined' && console.error) {
console.error('DisplayMath typesetting error:', err)
}
})
// If MathJax is loaded but typesetPromise isn't ready yet, retry after a short delay
} else if (mathRef.current && (window as any).MathJax) {
setTimeout(typesetMath, 50)
}
}
// Check if we're in a browser environment and MathJax is already loaded
if (typeof window !== 'undefined' && (window as any).MathJax) {
typesetMath()
} else {
// If MathJax isn't loaded yet, poll for it with exponential backoff
const checkMathJax = () => {
if ((window as any).MathJax && (window as any).MathJax.typesetPromise) {
typesetMath()
} else {
setTimeout(checkMathJax, 100)
}
}
// Start polling for MathJax to load
setTimeout(checkMathJax, 100)
}
}, [children])
return (
// Div element that will contain the LaTeX and be processed by MathJax
<div
ref={mathRef}
className={`display-math ${centered ? 'text-center' : ''} ${className}`}
style={{ margin: '1rem 0', width: '50%', marginLeft: 'auto', marginRight: 'auto' }}
dangerouslySetInnerHTML={{ __html: `$$${children}$$` }}
/>
)
}
export default DisplayMathWith this last file, your final project directory should now look like this:
mathjax-nextjs/
|--src/
|----app/
|------components/ (new directory to store our custom components)
|--------InlineMath.tsx (new file)
|--------DisplayMath.tsx (new file)
|------globals.css
|------layout.tsx (updated with the needed MathJax configuration)
|------page.tsx (updated with examples to test rendering)
|--types/
|----mathjax.d.ts (new file)
|--public/
|--package.json
|--tsconfig.json
|--next.config.ts (updated with the webpack configuration)
|--README.md
What we’ll accomplish: Create a comprehensive demonstration page that shows how to use our custom MathJax components with various types of mathematical expressions.
Why this step is needed: We need to test our components and provide examples of how to use them. This page will showcase both simple and complex mathematical expressions, demonstrating the full capabilities of our MathJax integration.
Update src/app/page.tsx to demonstrate the components:
import React from 'react'
import InlineMath from './components/InlineMath'
import DisplayMath from './components/DisplayMath'
export default function Home() {
return (
<main>
<h1>MathJax v4 + Next.js Test</h1>
<div>
<h2>Inline Math Test</h2>
<p>
Einstein's equation: <InlineMath>E = mc^2</InlineMath>
</p>
<p>
Pythagorean theorem: <InlineMath>a^2 + b^2 = c^2</InlineMath>
</p>
</div>
<div>
<h2>Display Math Test</h2>
<div>
<DisplayMath>E = mc^2</DisplayMath>
</div>
<div>
<DisplayMath>{`
\\sum_{i=1}^{n} i = \\frac{n(n+1)}{2}
`}</DisplayMath>
</div>
</div>
<div>
<h2>Colored Math Examples</h2>
<h3>Inline Colored Math</h3>
<p>
<InlineMath>{`E = \\color{red}{mc^2}`}</InlineMath>
</p>
<p>
<InlineMath>{`\\color{blue}{x^2} + \\color{green}{y^2} = \\color{red}{z^2}`}</InlineMath>
</p>
<p>
<InlineMath>{`f(x) = \\color{red}{x^2} + \\color{blue}{2x} + \\color{green}{1}`}</InlineMath>
</p>
<h3>Display Colored Math</h3>
<DisplayMath>
{`\\color{red}{E} = \\color{blue}{mc^2} + \\color{green}{\\frac{1}{2}mv^2}`}
</DisplayMath>
<DisplayMath>
{`\\frac{\\color{red}{numerator}}{\\color{blue}{denominator}} = \\color{green}{result}`}
</DisplayMath>
<DisplayMath>
{`\\color{red}{x}^{\\color{blue}{2}} + \\color{green}{y}_{\\color{magenta}{i}} = \\color{cyan}{z}`}
</DisplayMath>
<h3>RGB and Hex Colors</h3>
<DisplayMath>
{`\\color[rgb]{0.5,0.8,0.2}{RGB Color} + \\color{#FF5733}{Hex Color}`}
</DisplayMath>
</div>
<div>
<h2>Advanced Math Examples</h2>
<h3>Matrices and Arrays</h3>
<DisplayMath>
{`
\\begin{pmatrix}
a & b \\\\
c & d
\\end{pmatrix}
`}
</DisplayMath>
<DisplayMath>
{`
\\begin{bmatrix}
1 & 2 & 3 \\\\
4 & 5 & 6 \\\\
7 & 8 & 9
\\end{bmatrix}
`}
</DisplayMath>
<DisplayMath>
{`
\\begin{vmatrix}
a & b \\\\
c & d
\\end{vmatrix} = ad - bc
`}
</DisplayMath>
<h3>Vectors</h3>
<DisplayMath>
{`
\\vec{v} = \\begin{pmatrix}
x \\\\
y \\\\
z
\\end{pmatrix}
`}
</DisplayMath>
<DisplayMath>
{`
\\mathbf{v} = v_x\\hat{i} + v_y\\hat{j} + v_z\\hat{k}
`}
</DisplayMath>
<h3>Aligned Equations</h3>
<DisplayMath>
{`
\\begin{align}
(a + b)^2 &= a^2 + 2ab + b^2 \\\\
&= a^2 + b^2 + 2ab \\\\
&= (a + b)(a + b)
\\end{align}
`}
</DisplayMath>
<DisplayMath>
{`
\\begin{align*}
x &= 2y + 3 \\\\
&= 2(y + 1.5) \\\\
&= 2z \\quad \\text{where } z = y + 1.5
\\end{align*}
`}
</DisplayMath>
<h3>Labeled Equations</h3>
<DisplayMath>
{`\\begin{equation} E = mc^2 \\label{eq:einstein} \\end{equation}`}
</DisplayMath>
<DisplayMath>
{`\\begin{equation} F = ma \\label{eq:newton} \\end{equation}`}
</DisplayMath>
<DisplayMath>
{`\\begin{equation} \\sum_{i=1}^{n} i = \\frac{n(n+1)}{2} \\label{eq:sum} \\end{equation}`}
</DisplayMath>
<p>Referencing equations: Einstein's equation ({`\\ref{eq:einstein}`}) and Newton's law ({`\\ref{eq:newton}`}).</p>
<h3>Cases and Piecewise Functions</h3>
<DisplayMath>
{`
f(x) = \\begin{cases}
x^2 & \\text{if } x > 0 \\\\
0 & \\text{if } x = 0 \\\\
-x^2 & \\text{if } x < 0
\\end{cases}
`}
</DisplayMath>
<h3>Tables</h3>
<DisplayMath>
{`
\\begin{array}{|c|c|c|}
\\hline
x & y & x + y \\\\
\\hline
1 & 2 & 3 \\\\
4 & 5 & 9 \\\\
7 & 8 & 15 \\\\
\\hline
\\end{array}
`}
</DisplayMath>
<h3>Complex Examples</h3>
<DisplayMath>
{`
\\begin{align}
\\nabla \\cdot \\mathbf{E} &= \\frac{\\rho}{\\epsilon_0} \\label{eq:gauss} \\\\
\\nabla \\cdot \\mathbf{B} &= 0 \\label{eq:gauss-mag} \\\\
\\nabla \\times \\mathbf{E} &= -\\frac{\\partial \\mathbf{B}}{\\partial t} \\label{eq:faraday} \\\\
\\nabla \\times \\mathbf{B} &= \\mu_0\\mathbf{J} + \\mu_0\\epsilon_0\\frac{\\partial \\mathbf{E}}{\\partial t} \\label{eq:ampere}
\\end{align}
`}
</DisplayMath>
<p>Maxwell's equations: {`\\ref{eq:gauss}`}, {`\\ref{eq:gauss-mag}`}, {`\\ref{eq:faraday}`}, and {`\\ref{eq:ampere}`}.</p>
</div>
</main>
)
}
dangerouslySetInnerHTML is Required in ComponentsMathJax needs to process raw HTML strings that contain LaTeX delimiters like $...$ or $$...$$. When you write:
// This doesn't work - JSX treats it as text
<InlineMath>E = mc^2</InlineMath>JSX treats E = mc^2 as plain text, not as LaTeX that needs MathJax processing.
The solution is to inject the LaTeX content as HTML so MathJax can process it:
// This works - MathJax can process the HTML
<span dangerouslySetInnerHTML={{ __html: `$E = mc^2$` }} />// 1. User writes: <InlineMath>E = mc^2</InlineMath>
// 2. Component converts to: <span dangerouslySetInnerHTML={{ __html: `$E = mc^2$` }} />
// 3. Browser renders: <span>$E = mc^2$</span>
// 4. MathJax scans DOM, finds $E = mc^2$, and replaces it with rendered mathThe <DisplayMath> component uses specific styling to optimize equation layout:
width: '50%'): Makes equations narrower, naturally bringing equation labels closermarginLeft: 'auto', marginRight: 'auto'): Centers the equations horizontallymargin: '1rem 0'): Adds consistent spacing above and below equationsCustomization: All these values can be modified in the component’s style prop to match your design preferences. For example, you could change the width to 75% for wider equations, adjust the margins for different spacing, or remove the styling entirely for full-width display.
// Import components
import InlineMath from './components/InlineMath'
import DisplayMath from './components/DisplayMath'
// Simple expressions (no special characters)
<InlineMath>E = mc^2</InlineMath>
<DisplayMath>E = mc^2</DisplayMath>
// Complex expressions (using template literals for backslashes)
<InlineMath>{`x = \\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a}`}</InlineMath>
<DisplayMath>{`
\\sum_{i=1}^{n} i = \\frac{n(n+1)}{2}
`}</DisplayMath>// Labeled equations
<DisplayMath>
{`\\begin{equation} E = mc^2 \\label{eq:einstein} \\end{equation}`}
</DisplayMath>
<DisplayMath>
{`\\begin{equation} F = ma \\label{eq:newton} \\end{equation}`}
</DisplayMath>
// Cross-references
<p>Einstein's equation ({`\\ref{eq:einstein}`}) and Newton's law ({`\\ref{eq:newton}`}).</p>// Named colors
<InlineMath>{`\\color{red}{text}`}</InlineMath>
// RGB colors
<DisplayMath>{`\\color[rgb]{0.5,0.8,0.2}{text}`}</DisplayMath>
// Hex colors
<InlineMath>{`\\color{#FF5733}{text}`}</InlineMath>// 2x2 matrix with parentheses
<DisplayMath>{`
\\begin{pmatrix}
a & b \\\\
c & d
\\end{pmatrix}
`}</DisplayMath>
// 3x3 matrix with brackets
<DisplayMath>{`
\\begin{bmatrix}
1 & 2 & 3 \\\\
4 & 5 & 6 \\\\
7 & 8 & 9
\\end{bmatrix}
`}</DisplayMath>
// Determinant
<DisplayMath>{`
\\begin{vmatrix}
a & b \\\\
c & d
\\end{vmatrix} = ad - bc
`}</DisplayMath>
// Table with borders
<DisplayMath>{`
\\begin{array}{|c|c|c|}
\\hline
x & y & x + y \\\\
\\hline
1 & 2 & 3 \\\\
4 & 5 & 9 \\\\
\\hline
\\end{array}
`}</DisplayMath>
// Vector with arrow
<DisplayMath>{`
\\vec{v} = \\begin{pmatrix}
x \\\\
y \\\\
z
\\end{pmatrix}
`}</DisplayMath>
// Bold vector with unit vectors
<DisplayMath>{`
\\mathbf{v} = v_x\\hat{i} + v_y\\hat{j} + v_z\\hat{k}
`}</DisplayMath>
// Vector magnitude
<DisplayMath>{`
\\|\\vec{v}\\| = \\sqrt{v_x^2 + v_y^2 + v_z^2}
`}</DisplayMath>
// Aligned equations with numbering
<DisplayMath>{`
\\begin{align}
(a + b)^2 &= a^2 + 2ab + b^2 \\\\
&= a^2 + b^2 + 2ab \\\\
&= (a + b)(a + b)
\\end{align}
`}</DisplayMath>
// Aligned equations without numbering
<DisplayMath>{`
\\begin{align*}
x &= 2y + 3 \\\\
&= 2(y + 1.5) \\\\
&= 2z \\quad \\text{where } z = y + 1.5
\\end{align*}
`}</DisplayMath>// Piecewise defined function
<DisplayMath>{`
f(x) = \\begin{cases}
x^2 & \\text{if } x > 0 \\\\
0 & \\text{if } x = 0 \\\\
-x^2 & \\text{if } x < 0
\\end{cases}
`}</DisplayMath>
// Multiple conditions
<DisplayMath>{`
g(x) = \\begin{cases}
2x + 1 & \\text{if } x < 0 \\\\
x^2 & \\text{if } 0 \\leq x < 1 \\\\
\\sqrt{x} & \\text{if } x \\geq 1
\\end{cases}
`}</DisplayMath>| Issue | Solution |
|---|---|
| Math not rendering | Check browser console for errors, ensure MathJax CDN is loading |
| TypeScript errors | Verify types/mathjax.d.ts file exists and is properly configured |
| Component not found | Verify import paths and component file names |
| Math not rendering | Check browser console for errors, ensure MathJax CDN is loading |
| Math not rendering | Check browser console for errors, ensure MathJax CDN is loading |
window.MathJax exists in consolemathjax-nextjs/
|--src/
|----app/
|------components/ (new directory to store our custom components)
|--------InlineMath.tsx (new file)
|--------DisplayMath.tsx (new file)
|------globals.css
|------layout.tsx (updated with the needed MathJax configuration)
|------page.tsx (updated with examples to test rendering)
|--types/
|----mathjax.d.ts (new file)
|--public/
|--package.json
|--tsconfig.json
|--next.config.ts (updated with the webpack configuration)
|--README.md
This tutorial provides a complete solution for integrating MathJax v4 with Next.js. The custom components offer a clean, developer-friendly way to display mathematical expressions while maintaining full TypeScript support.
Key features include:
The solution is production-ready and can be extended with additional features like custom fonts, advanced styling, and performance optimizations based on your specific requirements.
All of the code, and the tutorial, were written using generative AI. The tutorial was slightly modified by hand.