JavaScript display engine for LaTeX, MathML, and AsciiMath mathematical notation in browsers and Node.js
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
MathJax supports two high-quality output formats: CommonHTML (HTML/CSS) and SVG (Scalable Vector Graphics). Both formats provide excellent rendering quality, accessibility support, and extensive customization options.
Render mathematics using HTML elements with CSS styling, providing excellent performance and integration with web content.
/**
* Get CommonHTML stylesheet
* @returns CSS stylesheet element for CommonHTML output
*/
function chtmlStylesheet(): Element;
/**
* Get metrics for CommonHTML element
* @param wrapper - Element wrapper to measure
* @param display - Whether element is in display mode
* @returns Metrics object with measurements
*/
function getMetricsFor(wrapper: Element, display: boolean): Metrics;Usage Examples:
// Convert to CommonHTML
const htmlElement = MathJax.tex2chtml('E = mc^2');
document.body.appendChild(htmlElement);
// Get stylesheet for styling
const stylesheet = MathJax.chtmlStylesheet();
document.head.appendChild(stylesheet);
// Get metrics for positioning
const metrics = MathJax.getMetricsFor(htmlElement, false);
console.log('Width:', metrics.em, 'em');Render mathematics as scalable vector graphics, providing perfect scaling and print quality.
/**
* Get SVG stylesheet
* @returns CSS stylesheet element for SVG output
*/
function svgStylesheet(): Element;
/**
* Get metrics for SVG element
* @param wrapper - Element wrapper to measure
* @param display - Whether element is in display mode
* @returns Metrics object with measurements
*/
function getMetricsFor(wrapper: Element, display: boolean): Metrics;Usage Examples:
// Convert to SVG
const svgElement = MathJax.tex2svg('\\frac{x^2}{2}');
document.body.appendChild(svgElement);
// Get stylesheet for SVG styling
const stylesheet = MathJax.svgStylesheet();
document.head.appendChild(stylesheet);
// Get SVG metrics
const metrics = MathJax.getMetricsFor(svgElement, true);
console.log('SVG dimensions:', metrics);interface CommonHTMLOptions {
/** Scaling factor for output */
scale?: number;
/** Minimum scaling factor */
minScale?: number;
/** Match container font height */
matchFontHeight?: boolean;
/** Inherit font for mtext elements */
mtextInheritFont?: boolean;
/** Inherit font for merror elements */
merrorInheritFont?: boolean;
/** Use MathML spacing rules */
mathmlSpacing?: boolean;
/** Attributes to skip when processing */
skipAttributes?: Record<string, boolean>;
/** Ex-height factor */
exFactor?: number;
/** Display alignment */
displayAlign?: 'left' | 'center' | 'right';
/** Display indentation */
displayIndent?: string;
/** Font URL for web fonts */
fontURL?: string;
/** Use adaptive CSS */
adaptiveCSS?: boolean;
}Configuration Example:
MathJax.config.chtml = {
scale: 1.2,
minScale: 0.5,
matchFontHeight: true,
mtextInheritFont: true,
merrorInheritFont: true,
mathmlSpacing: true,
exFactor: 0.5,
displayAlign: 'center',
displayIndent: '2em',
fontURL: 'https://cdn.jsdelivr.net/npm/mathjax@4/fonts/woff-v2',
adaptiveCSS: true,
skipAttributes: {
'data-semantic-type': true,
'data-semantic-role': true
}
};interface SVGOptions {
/** Scaling factor for output */
scale?: number;
/** Minimum scaling factor */
minScale?: number;
/** Inherit font for mtext elements */
mtextInheritFont?: boolean;
/** Inherit font for merror elements */
merrorInheritFont?: boolean;
/** Use MathML spacing rules */
mathmlSpacing?: boolean;
/** Attributes to skip when processing */
skipAttributes?: Record<string, boolean>;
/** Ex-height factor */
exFactor?: number;
/** Display alignment */
displayAlign?: 'left' | 'center' | 'right';
/** Display indentation */
displayIndent?: string;
/** Font caching mode */
fontCache?: 'local' | 'global' | 'none';
/** Local ID prefix */
localID?: string;
/** Include internal speech titles */
internalSpeechTitles?: boolean;
}Configuration Example:
MathJax.config.svg = {
scale: 1.0,
minScale: 0.5,
mtextInheritFont: false,
merrorInheritFont: false,
mathmlSpacing: true,
exFactor: 0.5,
displayAlign: 'center',
displayIndent: '0',
fontCache: 'local',
localID: 'MJX-SVG',
internalSpeechTitles: true,
skipAttributes: {
'data-mml-node': true
}
};CommonHTML output uses web fonts for optimal rendering:
// Font configuration
MathJax.config.chtml = {
fontURL: 'https://cdn.jsdelivr.net/npm/mathjax@4/fonts/woff-v2',
adaptiveCSS: true,
matchFontHeight: true
};
// Custom font paths
MathJax.config.chtml = {
fontURL: '/static/mathjax-fonts',
fontFormat: 'woff2' // woff, woff2, otf, ttf
};
// Disable web fonts (use system fonts)
MathJax.config.chtml = {
fontURL: null
};SVG output embeds font information directly:
// Font caching options
MathJax.config.svg = {
fontCache: 'local', // Cache fonts per element
// fontCache: 'global', // Global font cache
// fontCache: 'none' // No font caching
};
// Font loading
MathJax.config.options = {
loadAllFontFiles: true // Preload all font files
};// Custom CSS for CommonHTML
const style = document.createElement('style');
style.textContent = `
.mjx-chtml {
font-size: 120%;
color: #333;
}
.mjx-chtml .mjx-math {
border: 1px solid #ddd;
padding: 5px;
border-radius: 3px;
}
.mjx-display {
text-align: center;
margin: 1em 0;
}
`;
document.head.appendChild(style);
// Custom CSS for SVG
const svgStyle = document.createElement('style');
svgStyle.textContent = `
.mjx-svg {
background: #f9f9f9;
border-radius: 4px;
}
.mjx-svg svg {
filter: drop-shadow(1px 1px 2px rgba(0,0,0,0.1));
}
`;
document.head.appendChild(svgStyle);// Configure display alignment
MathJax.config.chtml = {
displayAlign: 'left',
displayIndent: '2em'
};
// Configure scaling
MathJax.config.svg = {
scale: 1.5,
minScale: 0.8
};
// Per-conversion options
const options = {
display: true,
scale: 1.2,
em: 16,
ex: 8,
containerWidth: 1200
};
const result = MathJax.tex2svg('x^2 + y^2 = z^2', options);// Choose format based on use case
function selectOptimalFormat(context) {
if (context.needsScaling) {
return 'svg'; // SVG scales perfectly
}
if (context.needsInteraction) {
return 'chtml'; // HTML integrates better with DOM
}
if (context.needsPrint) {
return 'svg'; // SVG prints at high quality
}
return 'chtml'; // Default to CommonHTML for web
}
// Dynamic format switching
async function renderWithOptimalFormat(math, context) {
const format = selectOptimalFormat(context);
if (format === 'svg') {
return MathJax.tex2svg(math);
} else {
return MathJax.tex2chtml(math);
}
}// Cache rendered mathematics
class MathCache {
constructor() {
this.cache = new Map();
}
render(expression, format, options = {}) {
const key = JSON.stringify({ expression, format, options });
if (this.cache.has(key)) {
return this.cache.get(key).cloneNode(true);
}
let result;
if (format === 'svg') {
result = MathJax.tex2svg(expression, options);
} else {
result = MathJax.tex2chtml(expression, options);
}
this.cache.set(key, result.cloneNode(true));
return result;
}
clear() {
this.cache.clear();
}
}
const mathCache = new MathCache();
const cached = mathCache.render('E = mc^2', 'svg');// Post-process CommonHTML output
function customizeCommonHTML(element) {
// Add custom attributes
element.setAttribute('data-math-type', 'equation');
// Add accessibility improvements
const mathElements = element.querySelectorAll('.mjx-math');
mathElements.forEach(math => {
math.setAttribute('role', 'img');
math.setAttribute('aria-label', 'Mathematical expression');
});
// Add custom styling
element.classList.add('custom-math');
return element;
}
// Apply custom processing
const rawHTML = MathJax.tex2chtml('x^2 + y^2 = z^2');
const customHTML = customizeCommonHTML(rawHTML);// Post-process SVG output
function customizeSVG(svgElement) {
const svg = svgElement.querySelector('svg');
// Add custom attributes
svg.setAttribute('data-math-rendered', 'true');
// Modify viewBox for custom sizing
const viewBox = svg.getAttribute('viewBox');
if (viewBox) {
const [x, y, width, height] = viewBox.split(' ').map(Number);
svg.setAttribute('viewBox', `${x-5} ${y-5} ${width+10} ${height+10}`);
}
// Add background rectangle
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('x', '0');
rect.setAttribute('y', '0');
rect.setAttribute('width', '100%');
rect.setAttribute('height', '100%');
rect.setAttribute('fill', '#f8f8f8');
rect.setAttribute('stroke', '#ddd');
svg.insertBefore(rect, svg.firstChild);
return svgElement;
}
// Apply SVG customization
const rawSVG = MathJax.tex2svg('\\int_0^\\infty e^{-x} dx');
const customSVG = customizeSVG(rawSVG);// Make mathematics responsive
function makeResponsive(mathElement, maxWidth = 600) {
const svg = mathElement.querySelector('svg');
if (svg) {
// Make SVG responsive
svg.style.maxWidth = '100%';
svg.style.height = 'auto';
// Scale down for small screens
const actualWidth = parseFloat(svg.getAttribute('width'));
if (actualWidth > maxWidth) {
const scale = maxWidth / actualWidth;
svg.style.transform = `scale(${scale})`;
svg.style.transformOrigin = 'left center';
}
}
return mathElement;
}
// Apply responsive design
const math = MathJax.tex2svg('\\sum_{i=1}^{n} \\frac{1}{i^2} = \\frac{\\pi^2}{6}');
const responsive = makeResponsive(math);// Print-optimized CSS
const printStyle = document.createElement('style');
printStyle.textContent = `
@media print {
.mjx-chtml {
font-size: 12pt;
color: black !important;
}
.mjx-svg svg {
max-width: 100% !important;
height: auto !important;
}
.mjx-display {
page-break-inside: avoid;
margin: 0.5em 0;
}
}
`;
document.head.appendChild(printStyle);// Dark mode mathematics
function applyDarkMode(isDark) {
const style = document.createElement('style');
style.id = 'mathjax-dark-mode';
if (isDark) {
style.textContent = `
.mjx-chtml {
color: #e0e0e0 !important;
}
.mjx-svg {
filter: invert(1) hue-rotate(180deg);
}
`;
} else {
style.textContent = '';
}
// Replace existing dark mode styles
const existing = document.getElementById('mathjax-dark-mode');
if (existing) {
existing.remove();
}
document.head.appendChild(style);
}
// Apply dark mode
applyDarkMode(true);interface Metrics {
/** Em size in pixels */
em: number;
/** Ex height in pixels */
ex: number;
/** Container width */
containerWidth: number;
/** Scale factor */
scale: number;
/** Line width setting */
lineWidth?: number;
/** Actual scale factor used */
scaleFactor?: number;
/** Font scaling factor */
fontScale?: number;
}Usage Examples:
// Get element metrics
const element = MathJax.tex2chtml('x^2 + y^2 = z^2');
const metrics = MathJax.getMetricsFor(element, false);
console.log('Em size:', metrics.em);
console.log('Ex height:', metrics.ex);
console.log('Scale:', metrics.scale);
// Use metrics for positioning
element.style.marginTop = `${metrics.ex * 0.5}px`;
element.style.marginBottom = `${metrics.ex * 0.5}px`;
// Responsive scaling based on metrics
const scaleFactor = Math.min(1.0, 800 / metrics.containerWidth);
element.style.transform = `scale(${scaleFactor})`;// Get bounding box information
function getMathBoundingBox(mathElement) {
const svg = mathElement.querySelector('svg');
if (svg) {
const viewBox = svg.getAttribute('viewBox');
if (viewBox) {
const [x, y, width, height] = viewBox.split(' ').map(Number);
return { x, y, width, height };
}
}
// For CommonHTML, use DOM measurements
const rect = mathElement.getBoundingClientRect();
return {
x: 0,
y: 0,
width: rect.width,
height: rect.height
};
}
// Position mathematics using bounding box
const math = MathJax.tex2svg('\\frac{a}{b}');
const bbox = getMathBoundingBox(math);
math.style.position = 'absolute';
math.style.left = `${100 - bbox.width / 2}px`;
math.style.top = `${50 - bbox.height / 2}px`;