mathlive: convertLatexToMarkup() doesn't work server-side

Description

I’m trying to use convertLatexToMarkup server-side, but it either won’t resolve the import, or tries to reference HTMLElement.

This seems like it might be related to https://github.com/arnog/mathlive/issues/1330, but the fix doesn’t seem to be making any difference.

Steps to Reproduce

Create a script like this, and run it through Node.js:

import {convertLatexToMarkup} from 'mathlive';

console.log(convertLatexToMarkup('x=\\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}'));

I get the following error:

import {convertLatexToMarkup} from 'mathlive';
        ^^^^^^^^^^^^^^^^^^^^
SyntaxError: Named export 'convertLatexToMarkup' not found. The requested module 'mathlive' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from 'mathlive';
const {convertLatexToMarkup} = pkg;

If I try changing it as suggested:

import pkg from 'mathlive';

const {convertLatexToMarkup} = pkg;
console.log(convertLatexToMarkup('x=\\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}'));

Then it fails with ReferenceError: HTMLElement is not defined.

Environment

This is called in render.mjs, using Node.js v19.1.0 and Mathlive v0.85.1.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 15 (13 by maintainers)

Most upvoted comments

If you are trying to render on the client side, you should be able to call new MathfieldElement() or new TextArea() for that matter. It sounds like you are configured to do server side rendering. When doing (React) server side rendering, the DOM is not available, and you cannot create dynamic DOM element. You can only render markup, i.e. <math-field> or <textarea> which will be hydrated on the client later.

To clarify another potential point of confusion, in the context of React (or Next.js) server side rendering refers to rendering the content of your page on the document, that is statically generating the markup for your page, then hydrating it on the client.

In the context of MathLive, server side rendering refers to the ability to create markup representing a LaTeX expression.

The MathfieldElement cannot be “rendered” on the server side, since it’s an interactive component, just like a TextArea element cannot be rendered on the server. Instead, you can create some markup (<math-field> and <textarea>, respectively) that get sent to the client and instantiated there.

Hope this helps clarify things a bit.

For anybody who came across my problem (using mathlive in NextJS), there are 3 solutions that I found

  1. Use dynamic import to import the component that uses mathlive with ssr turned off (not mathlive itself)
const MathInput = dynamic(() => import('path/to/your/component').then(mod => mod.MathInput), { ssr: false })
  1. Import mathlive in useEffect

const mfeRef = useRef<MathfieldElement>();
const [isMfeReady, setIsMfeReady] = useState(false);

useEffect(() => {
    (async () => {
      const MathfieldElement = (await import('mathlive')).MathfieldElement;
      const mfe = new MathfieldElement(/* config option here */);
      mfeRef.current = mfe;
      // replace children of div with mfe
      setIsMfeReady(true);
    })();
  }, []);
  1. Use callback ref
  const mfeRef = useRef<MathfieldElement>();
  const [isReady, setIsReady] = useState(false);

  const mathInputCallbackRef = useCallback(async (el: HTMLDivElement) => {
    const MathfieldElement = (await import('mathlive')).MathfieldElement;
    const mfe = new MathfieldElement(/* config option here */);
    el?.replaceChildren(mfe);
    mfeRef.current = mfe;
    setIsReady(true);
  }, []);

  return <div ref={mathInputCallbackRef} />;

There is now a version of MathLive specifically for server-side rendering: /dist/mathlive-ssr.min.mjs. The server-side rendering code has also been isolated and no longer require jsdom: src/public/mathlive-ssr.ts.