| SvelteKit and Mathjax
One way of getting SvelteKit and the Mathjax library to play nice with each other. I did this in the process of creating an online math game with a friend.
Feb

I recently needed to build a component in Svelte which would allow for real-time rendering of math equations. I initially turned to using KaTeX, given that’s it’s lighter weight than the alternatives, but after some fiddling I ended up using MathJax instead. I do believe the solution below would work just fine with KaTeX, though.

In my initial search for a solution I came across this repl which showed how to pass and load it via a component. This is done through the onMount function, which needs to load a script to parse the page, and which will be called on the first load of the component. This works well for static pages, where only a single pass is needed, but fails if you update the content via a bind:value or something similar.

Looking further I came across this example which describes how to do real-time editing, but I couldn’t work this into my components. This may not be the best strategy, but what I ended up turning to is reloading the component on each edit, using key as in the code below (code in repl). Key blocks are handy in that they create and destroy the content they contain every time the value is updated. In this case it forces the tex-chtml.js script to rerun, updating our input.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
//App.svelte

<script>
	import MathJax from './MathJax.svelte';
	
	let math = '$$x^3$$';
</script>

<div>
	<input bind:value={math}>
</div>
{#key math}
	<MathJax {math}></MathJax>
{/key}

The MathJax component:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>
	import { createEventDispatcher, onMount } from 'svelte';

	export let math;
	let mathContent ='';
	const dispatch = createEventDispatcher();
	
	onMount(() => {
		let script = document.createElement('script');
        script.src = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js";
        document.head.append(script);
		
		script.onload = () => {
            MathJax = {
                tex: {inlineMath: [['$', '$'], ['\\(', '\\)']]},
                svg: {fontCache: 'global'}
            };
        };
	});
</script>

<p>
	{math}
</p>

This is pretty hacky, and I’d like to find a better way of reloading without having to refresh the component, but it works for the time being.