MathJax 2 to 3 Migration Guide (and What Changes in MathJax 4)

MathJax 3 is a complete rewrite of MathJax 2, so upgrading means changing how you load, configure and call MathJax. MathJax 4 keeps the version 3 API and mainly changes fonts and line breaking. This guide walks through each change with before-and-after code.

Compare your content in MathJax 2, 3 and 4 →

Step 1: Replace the script tag

MathJax 2 loads MathJax.js with a ?config= parameter. MathJax 3 and 4 load a single combined component file instead. Pick the file that matches your old configuration:

MathJax 2 ?config= MathJax 3 and 4 file
TeX-AMS_CHTML or TeX-AMS_HTML tex-chtml.js
TeX-AMS_SVG tex-svg.js
TeX-MML-AM_CHTML or TeX-AMS-MML_HTMLorMML tex-mml-chtml.js (AsciiMath is not included; see step 4)
MML_CHTML mml-chtml.js

The CDN paths differ slightly between versions. MathJax 3 files live in an es5/ folder:

<!-- MathJax 2 -->
<script src="https://cdn.jsdelivr.net/npm/mathjax@2.7.9/MathJax.js?config=TeX-AMS_CHTML"></script>

<!-- MathJax 3 -->
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>

<!-- MathJax 4 -->
<script src="https://cdn.jsdelivr.net/npm/mathjax@4/tex-chtml.js"></script>

Step 2: Convert your configuration

MathJax 2 reads configuration from a text/x-mathjax-config script that calls MathJax.Hub.Config(). MathJax 3 and 4 read a plain window.MathJax object, which must be defined before the MathJax script loads.

Before (MathJax 2)

<script type="text/x-mathjax-config">
  MathJax.Hub.Config({
    tex2jax: {
      inlineMath: [['$', '$'], ['\\(', '\\)']],
      processEscapes: true
    },
    TeX: {
      extensions: ['mhchem.js'],
      Macros: { RR: '{\\bf R}' }
    }
  });
</script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@2.7.9/MathJax.js?config=TeX-AMS_CHTML"></script>

After (MathJax 3 and 4)

<script>
  window.MathJax = {
    tex: {
      inlineMath: [['$', '$'], ['\\(', '\\)']],
      macros: { RR: '{\\bf R}' }
    }
  };
</script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>

What changed in this example:

Step 3: Update code that re-renders math

If your page adds math after loading (for example in a React, Vue or Angular app), the typesetting calls change too:

MathJax 2 MathJax 3 and 4
MathJax.Hub.Queue(["Typeset", MathJax.Hub, el]) MathJax.typesetPromise([el])
MathJax.Hub.Typeset() MathJax.typeset() (synchronous)
MathJax.Hub.Register.StartupHook("End", fn) MathJax.startup.promise.then(fn)
No equivalent MathJax.typesetClear([el]) before removing or replacing typeset math

typesetPromise calls must not overlap. A safe pattern is to chain every call onto the startup promise:

function typeset(elements) {
  MathJax.startup.promise = MathJax.startup.promise
    .then(() => MathJax.typesetPromise(elements))
    .catch((err) => console.error('Typeset failed:', err));
  return MathJax.startup.promise;
}

Step 4: Check extensions and behaviour changes

Step 5: What changes in MathJax 4

Code written for MathJax 3 mostly works in MathJax 4 unchanged. The differences you'll notice:

Test before you switch

The fastest way to catch rendering differences is to paste real content from your site into the MathJax live preview, which renders it in MathJax 2.7.9, 3.2.2 and 4.1.3 side by side.

Try an example in the live preview →