Using MathJax with React

Published on 4 June 2020

This post describes how to use React (Typescript) and MathJax.

There are a few performance optimizations:

  • React.memo makes it so that even if the parent rerenders but the Latex does not change, the component does not rerender (reference).
  • The MathJax typeset() is only called when rawLatex changes. Technically, this is unnecessary, as the React.memo should mean that it only rerenders when the prop changes.

You can either load MathJax in the head or load it dynamically in other ways. With Next.js, I was running into issues with using the <Head> tag; see how to dynamically load scripts in React.

/**
 * Combined with dynamic script loading, this component allows us to render
 * MathJax. Uses MathJax 3.0. It takes an approach similar to the article below:
 *
 * https://engineering.classpro.in/render-latex-in-react-using-mathjax-f9742504678
 *
 * but then adapts based on MathJax 3.0:
 *
 * http://docs.mathjax.org/en/latest/web/typeset.html#handling-asynchronous-typesetting
 */

import React from 'react';

const getMathJax = () => (window as any).MathJax;

const typeset = (selector: () => HTMLElement) => {
  const mathJax = getMathJax();
  // If MathJax script hasn't been loaded yet, then do nothing.
  if (!mathJax) {
    return null;
  }
  mathJax.startup.promise = mathJax.startup.promise
    .then(() => {
      selector();
      return mathJax.typesetPromise();
    })
    .catch((err: any) => console.error(`Typeset failed: ${err.message}`));
  return mathJax.startup.promise;
};

interface LatexProps {
  rawLatex: string;
}

const Latex: React.FC<LatexProps> = ({ rawLatex }) => {
  const ref = React.createRef<HTMLSpanElement>();
  React.useEffect(() => {
    typeset(() => ref.current!);
  }, [rawLatex]);

  return <span ref={ref}>{rawLatex}</span>;
};

export default React.memo(Latex);

Comments