Using MathJax Components in Node
It is possible to use MathJax in a node application in essentially the
same way that it is used in a browser. In particular, you can load
MathJax components and configure MathJax using a global
MathJax object and load a combined component file or the startup component
via node’s import or require() commands.
First get a copy of the MathJax code library.
Here, we will assume you have used npm or pnpm to install the
@mathjax/src@4 package. You will need to change the require()
or import statements accordingly in the examples below if you have
loaded mathjax@4 or obtained MathJax from the MathJax-src
GitHub repository.
In MathJax, the loading of components is asynchronous, and so you may
need to use promises or the await command to mediate the flow of
your program, particularly program startup. Once MathJax’s components
are loaded, however, you can call the non-promise-based functions, but
should use the promise-based ones if you want to support autoloading
of extensions, the \require macro in TeX input, or the v4 fonts
with larger character coverage.
Warning
In MathJax v4, with the introduction of new fonts that include many
more characters than the original MathJax TeX fonts did, the fonts
have been broken into smaller pieces so that your readers don’t
have to download the entire font and its data for characters that
may never be used. That means that typesetting mathematics may
need to operate asynchronously even if the TeX doesn’t include
\require or any auto-loaded extensions, as the output itself
could need extra font data files to be loaded. Thus in version 4,
it is always best to use the promise-based commands.
The synchronous examples show how to operate synchronously from the outset, if that is required.
Configuring and Loading Components in Node
As with MathJax in a browser, using MathJax components in a node
application consists of two steps: configuring MathJax, and Loading a
MathJax combined component to process the configuration. Just as in a
browser, the configuration is specified in the global
MathJax variable.
Configuration for Node vs. the Web
If you are using MathJax components as part of a larger application
that will be bundled for use in a web browser, then your MathJax
configuration should be the same as the configuration you would use if
you were loading MathJax into the web page directly, with one
exception: you may need to provide the URL where MathJax should load
any extensions that are needed once MathJax is running. Normally,
MathJax determines that URL based on the src attribute of the
script tag that loaded it, but since MathJax is part of your
larger application, that URL is probably not appropriate, so you will
need to specify the correct one yourself.
This is done using the mathjax property of the paths section
in the loader block of your MathJax configuration. For example,
global.MathJax = {
loader: {
paths: {
mathjax: 'https://cdn.jsdelivr.net/npm/mathjax@4'
}
}
}
would set things up so that extensions would be loaded from the
jsDelivr CDN. You could, of course, make the extensions available
on your own server and set the URL to point to that.
If you are using MathJax components as part of a server-side or
command-line application, you should set the mathjax path to
@mathjax/src/bundle instead:
global.MathJax = {
loader: {
paths: {
mathjax: '@mathjax/src/bundle'
}
}
}
so that MathJax will take additional components from the bundle
directory.
Note
In version 4, the bundle directory replaces the es5 directory from version 3.
For non-browser applications, there are two additional steps you need
to take. First, you must tell MathJax to use import() or
require() as the mechanism for loading external files, and second,
you need to load a non-browser DOM adaptor. MathJax provides a light-weight DOM
implementation (called liteDOM) that is sufficient for MathJax’s
needs without unnecessary overhead, so you probably want to use
that. If you need a more full-featured DOM implementation, you can use
another one, such as jsdom or linkedom (MathJax does provide
adaptors for these). It is even possible to use puppeteer with
headless Chrome in order to be able to access a full DOM
implementation from node.
Both of these features are set in the loader block of your MathJax
configuration object, as illustrated in the sections below.
Configuring MathJax for Use with import
Because the configuration must be in place before the MathJax
component is loaded, if you are using import commands rather than
require(), that means you either need to put the MathJax
configuration into a separate file to be imported before MathJax
itself, or you need to use the promise-based import() function to
load MathJax.
So you could create a file called mathjax-config.mjs containing
global.MathJax = {
loader: {
paths: {mathjax: '@mathjax/src/bundle'},
load: ['adaptors/liteDOM'],
require: (file => import(file))
},
// additional configuration here
};
and then use
import './mathjax-config.js';
import '@mathjax/src/bundle/tex-chtml.js';
await MathJax.startup.promise;
// your code that uses MathJax here
MathJax.done();
to load the tex-chtml combined component with that configuration,
and wait for MathJax to set itself up.
Note
In MathJax v4, the speech generation is performed in web-workers
(in the browser) or worker-threads (in node applications), and once
these are started, they will prevent the node application from
ending if they are not shut down. So v4 includes the
MathJax.done() function that terminates the workers,
thus allowing the node program to end. You should call this when
your program is ready to end so that it can shut down properly.
Alternatively, you could do
global.MathJax = {
loader: {
paths: {mathjax: '@mathjax/src/bundle'},
load: ['adaptors/liteDOM'],
require: (file => import(file))
},
// additional configuration here
};
await import('@mathjax/src/bundle/tex-chtml.js');
await MathJax.startup.promise;
// your code that uses MathJax here
MathJax.done();
to include the configuration in-line before loading the tex-chtml
component.
Note
ES6 modules usually use import, and require() is not
available, but it is possible to define require() if you are
making a command-line or server-side application. MathJax provides a
file that does that for you, so if you add
import '@mathjax/src/bundle/require.mjs';
to your code, you can then use require() as described in the
following section.
Configuring the Speech Locale
The default speech language is English, and the default Braille code
is Nemeth. You can use the sre block of the
options section of your MathJax configuration to specify a
different locale or Braille version, as illustrated below.
global.MathJax = {
loader: {
paths: {mathjax: '@mathjax/src/bundle'},
load: ['adaptors/liteDOM'],
require: (file) => import(file)
},
options: {
sre: {
locale: 'de'
}
}
// additional configuration here
};
await import('@mathjax/src/bundle/tex-chtml.js');
await MathJax.startup.promise;
// your code that uses MathJax here
MathJax.done();
which configures MathJax to produce speech strings in German rather than English.
Configuring MathJax for Use with require()
To use MathJax components in a CommonJS module, first set up the
MathJax configuration, and then require() the combined component
you want to load. So you can do
MathJax = {
loader: {
paths: {mathjax: '@mathjax/src/bundle'},
load: ['adaptors/liteDOM'],
require: require
},
// additional configuration here
};
require('@mathjax/src/bundle/tex-chtml.js');
MathJax.startup.promise
.then(() => {
//your MathJax code here
})
.catch((err) => console.error(err.message))
.then(() => MathJax.done());
to configure MathJax for use with the tex-chtml combined
component, and then wait for MathJax to start up, perform your
commands (with error trapping), and then shut down MathJax.
Loading Individual Components
If you are using MathJax components in a server-side or command-line
application, the combined components that MathJax provides may include
components that you don’t need (such as the menu code and expression
explorer). So you may want to configure MathJax explicitly to use
only the components that you need. You do this by listing the needed
components in the load array of the loader section of the
MathJx configuration, and then load the startup.js module rather
than a combined component.
For example,
global.MathJax = {
loader: {
paths: {mathjax: '@mathjax/src/bundle'},
load: ['input/tex', 'output/svg', 'adaptors/liteDOM'],
require: (file => import(file)),
},
output: {font: 'mathjax-newcm'}
}
await import('@mathjax/src/bundle/startup.js');
await MathJax.startup.promise;
would load only the TeX input jax and the SVG output jax, along with
the liteDOM adaptor, but without loading the menu code, the
assistive tools, or any other components. Because the input/tex
component includes the require and autoload extensions, the TeX that you process could still load
TeX extensions that are needed.
Because the output/svg component does not include a font, you need
to configure that separately in the output section of the
configuration, as shown.
Loading MathJax Components from Source
The examples above all load the webpacked versions of MathJax’s
components. It is possible to load the files from the source .js
files in the mjs or cjs directories, which may be useful if
you are modifying the MathJax source files and want to test your
changes without having to repack all the components.
To do this, you should set the source mapping in the
loader section of the MathJax configuration, and then load
the combined component from its source file in the components
directory rather than the bundle directory. The source.js
file in components/mjs or components/cjs directory contains
the mapping of component names to their source definitions, and you
can use that to set the source field of your MathJax
configuration.
You can obtain the source.js file using
@mathjax/src/components/js/source.js, and it will select the
mjs or cjs directory depending on whether you use import or
require() to load it. So for use in ES6 modules, you can do
import {source} from '@mathjax/src/components/js/source.js';
import '@mathjax/src/bundle/require.mjs'; // needed by speech-rule engine
global.MathJax = {
loader: {
paths: {mathjax: '@mathjax/src/bundle'},
load: ['adaptors/liteDOM'],
require: (file => import(file)),
source: source
}
// additional configuration here
}
await import(source['tex-chtml']);
await MathJax.startup.promise;
// your code that uses MathJax here
MathJax.done();
while for CommonJS modules, you can do
const {source} = require('@mathjax/src/components/js/source.js');
MathJax = {
loader: {
paths: {mathjax: '@mathjax/src/bundle'},
load: ['adaptors/liteDOM'],
require: require,
source: source
}
// additional configuration here
}
require(source['tex-chtml']);
MathJax.startup.promise
.then(() => {
//your MathJax code here
})
.catch((err) => console.error(err.message))
.then(() => MathJax.done());
Calling MathJax from Components
Once you have loaded a combined component file (or the startup
component), you can use the normal MathJax commands to typeset
mathematics. For example, in a browser application, you can call
MathJax.typesetPromise() to typeset the page.
For a command-line application, you could do
const EM = 16; // size of an em in pixels
const EX = 8; // size of an ex in pixels
const WIDTH = 80 * EM; // width of container for linebreaking
function typeset(math, display = true) {
return MathJax.tex2svgPromise(math, {
display: display,
em: EM,
ex: EX,
containerWidth: WIDTH
}).then((node) => {
const adaptor = MathJax.startup.adaptor;
return(adaptor.serializeXML(adaptor.tags(node, 'svg')[0]));
}).catch(err => console.error(err));
}
to define a typeset() command that takes a TeX string and an
optional boolean that specifies whether the typesetting should be in
display mode or in-line mode and returns a promise that is resolved
when the typesetting is complete (while handling any waiting that had
to be done to load extensions, fonts, etc.).
The typeset() promise returns the serialized SVG output, so that you could do
const svg = await typeset('\\sqrt{1+x^2}');
to get the SVG output. See the Creating Stand-Alone SVG Images section for an example of generating SVG images that handles the CSS needed by some expressions in MathJax.
Examples of Components in Node
The following combines some of the ideas described above into a
single, complete example of a command-line tool that takes three
arguments: a TeX string to typeset, the language locale to use, and
the Braille format to use. The last two are optional, and default to
en and nemeth.
1global.MathJax = {
2 loader: {
3 paths: {mathjax: '@mathjax/src/bundle'},
4 load: ['adaptors/liteDOM'],
5 require: (file) => import(file)
6 },
7 options: {
8 sre: {
9 locale: process.argv[3] || 'en',
10 braille: process.argv[4] || 'nemeth'
11 }
12 },
13 output: {
14 linebreaks: {
15 inline: false,
16 }
17 },
18 // additional configuration here
19};
20
21await import('@mathjax/src/bundle/tex-svg.js');
22await MathJax.startup.promise;
23
24const EM = 16; // size of an em in pixels
25const EX = 8; // size of an ex in pixels
26const WIDTH = 80 * EM; // width of container for linebreaking
27
28function typeset(math, display = true) {
29 return MathJax.tex2svgPromise(math, {
30 display: display,
31 em: EM,
32 ex: EX,
33 containerWidth: WIDTH
34 }).then((node) => {
35 const adaptor = MathJax.startup.adaptor;
36 return(adaptor.serializeXML(adaptor.tags(node, 'svg')[0]));
37 }).catch(err => console.error(err));
38}
39
40const math = process.argv[2] || '';
41const svg = await typeset(math);
42console.log(svg);
43
44MathJax.done();
See the Creating Stand-Alone SVG Images section for an example of generating SVG images that handles the CSS needed by some expressions in MathJax.
See the MathJax node demos for more examples of how to use MathJax from a node application. In particular, see the component-based examples for illustrations of how to configure and load MathJax components.