# Dynamic MathJax typesetting in Svelte

*(TL;DR;give-me-the-code: Dynamic MathJax rendering — Svelte REPL)*

I am building a quiz platform for the startup I am working on right now. The questions in the quiz often contain LaTeX math elements which need to be typeset by MathJax.

## The setup

MathJax and its config is brought in with the `<svelte:head>`

tag, like this:

```
<svelte:head>
<script>
window.MathJax = {
tex: {
inlineMath: [
["$", "$"],
["\\(", "\\)"],
],
displayMath: [["$$", "$$"]],
},
svg: {
fontCache: "global",
},
startup: {
typeset: false,
},
};
</script>
<script
id="MathJax-script"
defer
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"
></script>
</svelte:head>
```

I have an array of questions (which are just HTML text with LaTeX expressions)
and a variable called `current`

, which keeps track of the index of the current
question. There are also couple of buttons to switch between the questions.

```
<script>
const current = 0;
const questions = [
`This is $ e^x $: <br /> $$ {\\bf{e}}^x = \\sum\\limits_{n = 0}^\\infty {\\frac{{{x^n}}}{{n!}}} $$`,
`And this is $ \\cos x $: <br /> $$ \\cos x = \\sum\\limits_{n = 0}^\\infty {\\frac{{{{\\left( { - 1} \\right)}^n}{x^{2n}}}}{{\\left( {2n} \\right)!}}} $$`,
`Finally, this is $ \\sin x $: <br /> $$ \\sin x = \\sum\\limits_{n = 0}^\\infty {\\frac{{{{\\left( { - 1} \\right)}^n}{x^{2n + 1}}}}{{\\left( {2n + 1} \\right)!}}} $$`,
];
</script>
<svelte:head>
...
</svelte:head>
<main>
<button on:click={() => current--}>Prev</button>
<button on:click={() => current++}>Next</button>
<p> {@html questions[current] } </p>
</main>
```

## The problem

MathJax runs the typesetter just once, as soon as it's loaded on the page. At this stage, the question text has not loaded in yet, so the typesetting doesn't take effect.

We need a way to make this *dynamic*, so that MathJax will re-typeset the text
when the question number (i.e., value of `current`

) changes.

## The solution

We can use two nifty tools that Svelte provides — the `use`

directive and the
`{#key}`

block — to solve this problem.

### Implementing `use:typesetMath`

The `use`

directive
calls the attached function (called an *action*) when the element is created.

We can use this to create an action that will check if MathJax has been loaded
and run the formatter (`window.MathJax.typesetPromise`

) on our element.

```
<script>
// ... //
// Action functions accept the HTML element as an argument when they are called
function typesetMath(node) {
const typesetPromise = window.MathJax.typesetPromise;
// Check if MathJax has actually loaded before calling `typesetPromise`
if (typesetPromise) typesetPromise([node]);
}
//...//
</script>
...
<p use:typesetMath>{@html questions[current] }</p>
...
```

Now, we need to make sure that this action is fired whenever the variable
`current`

changes.

### Setting up the `{#key}`

block

The `{#key}`

block will cause the elements inside it to re-render whenever the
*key* (in this case, the variable `current`

) changes, which then goes ahead and
triggers the `typesetMath`

action.

```
{#key current}
<p use:typesetMath>{@html questions[current] }</p>
{/key}
```

So far so good! Everything should work more-or-less as expected now, but there is one little bug.

The typesetting might not happen properly for the question that is shown by
default, because MathJax might not have been loaded by the time the `typesetMath`

call is made, and the function exits without doing anything.

To fix this, we can use the `else`

branch in the conditional in `typesetMath`

to
call itself recursively (with a small delay) until MathJax is loaded.

```
function typesetMath(node) {
const typesetPromise = window.MathJax.typesetPromise;
if (typesetPromise) typesetPromise([node]);
// recursively call itself every 500ms until MathJax loads
else setTimeout(() => typesetMath(node), 500);
}
```

And that's it! That's how I hacked it together. It is not an elegant fix but it works pretty well for now.

## Click here for the full code.

```
<script>
import { afterUpdate } from 'svelte';
let current = 0;
const questions = [
`This is $ e^x $: <br /> $$ {\\bf{e}}^x = \\sum\\limits_{n = 0}^\\infty {\\frac{{{x^n}}}{{n!}}} $$`,
`And this is $ \\cos x $: <br /> $$ \\cos x = \\sum\\limits_{n = 0}^\\infty {\\frac{{{{\\left( { - 1} \\right)}^n}{x^{2n}}}}{{\\left( {2n} \\right)!}}} $$`,
`Finally, this is $ \\sin x $: <br /> $$ \\sin x = \\sum\\limits_{n = 0}^\\infty {\\frac{{{{\\left( { - 1} \\right)}^n}{x^{2n + 1}}}}{{\\left( {2n + 1} \\right)!}}} $$`
]
function typesetMath(node) {
const typesetPromise = window.MathJax.typesetPromise;
if (typesetPromise) typesetPromise([ node ]);
else setTimeout(() => typesetMath(node), 500)
}
</script>
<svelte:head>
<script>
window.MathJax = {
tex: {
inlineMath: [
['$', '$'],
['\\(', '\\)']
],
displayMath: [['$$', '$$']]
},
svg: {
fontCache: 'global'
},
startup: {
typeset: false
}
};
</script>
<script
id="MathJax-script"
defer
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"
></script>
</svelte:head>
<main>
<button disabled={current===0} on:click={() => current--}>Prev</button>
<button disabled={current===questions.length-1} on:click={() => current++}>Next</button>
{#key current}
<p use:typesetMath>
{@html questions[current] }
</p>
{/key}
</main>
```

I have also created a Svelte REPL if you want to play around with this.

## Comments

Comment via ...