Search code examples
mathjax

How to import MathJax in ESM modules?


I'm trying to import and use MathJax within a browser-based ESM module using various flavors of importing including the following:

import * as MathJax from 'mathjax/es5/tex-svg'; 
import 'mathjax/es5/tex-svg'; 

Attempted usage is as follows:

MathJax.tex2svg(String.raw`${text}`);

This throws a TypeError stating MathJax2.tex2svg is not a function. I installed MathJax using:

npm install mathjax

How can one import and use MathJax in a browser-based ESM environment where the dependency is managed by npm. If it's at all helpful, I'm using esbuild for bundling.


Solution

  • Because MathJax components (like tex-svg.js) accept a configuration that could specify additional components to load, and the loading of those components is asynchronous when done in a browser, MathJax's startup process is asynchronous and mediated by javascript promises. That is, even though your import command does make MathJax available, it is not yet fully set up at that point. The startup code is run inside a promise, and that promise will not execute until after the next time javascript is idle. Your call to MathJax.tex2svg() occurs before that next idle time (as the browser performs the two scripts without being idle in between), and so it is being called before MathJax is fully set up. The commands like tex2svg() are added to the MathJax object by the startup code based on the components that are loaded, so that has to occur after all the components are loaded, which means it must be in the promise that runs after asynchronous loading is complete, and that means it is after the next idle time.

    There are several ways to hook into MathJax's loading sequence. One would be to use the MathJax.startup.promise, which is resolved after MathJax is set up and does its initial typesetting of the page. So you could try

    MathJax.startup.promise.then(() => MathJax.tex2svg(text));
    

    instead.

    Alternatively, you can use the MathJax object to configure MathJax before loading tex-svg.js, and use the startup.ready() option to provide a callback when MathJax is fully set up. E.g., make a file mathjax-config.js containing

    window.MathJax = {
      startup: {
        ready() {
          MathJax.myStartup();
          MathJax.startup.defaultReady();
        }
      }
    };
    

    and then do

    import 'mathjax-config.js';
    import 'mathjax/es5/tex2svg';
    
    MathJax.myStartup = () => {
      console.log(MathJax.tex2svg(text));
    };
    

    Since MathJax.startup.ready() will not be called until after the first javascript idle time, your MathJax.myStartup() function will get to be defined before the ready function is called.

    Note that tex-svg.js does not have any exports, so you should just do

    import 'mathjax/es5/tex-svg';
    

    not

    import * as MathJax from 'mathjax/es5/tex-svg';
    

    The MathJax is a global variable that will become available after tex-svg.js is imported.

    Also, String.raw`${text}` is equivalent to just text, so no need for the extra complication, unless you just mean ${text} to represent some arbitrary text literal you would use in place of it.