remarkjs/remark-math

rehype-mathjax: CHTML not rendering

FunkMonkey opened this issue · 10 comments

Initial checklist

Affected packages and versions

4.0.3

Link to runnable example

No response

Steps to reproduce

import React from 'react';
import ReactMarkdown from 'react-markdown';
import rehypeMathjaxChtml from 'rehype-mathjax/chtml';
import remarkMath from 'remark-math';

export interface IExpressionProps {
  expression: string;
  inline?: boolean;
}

export function Expression({ expression, inline }: IExpressionProps): React.ReactElement {
  const text = inline ? `$${expression}$` : `$$${expression}$$`;
  return (
    <ReactMarkdown
      remarkPlugins={[remarkMath]}
      rehypePlugins={[
        [
          rehypeMathjaxChtml,
          {
            scale: 4,
            chtml: {
              fontURL: 'https://cdn.jsdelivr.net/npm/mathjax@3.2.2/es5/output/chtml/fonts/woff-v2',
            },
          },
        ],
      ]}
    >
      {text}
    </ReactMarkdown>
  );
}

Runtime: Building via Vite for the browser on Node18.

Expected behavior

Equations should be rendered

Actual behavior

Equations are not rendered with rehypeMathjaxChtml, but are rendered when using the default SVG renderer instead.

The problem is most likely that the generated HTML-code contains classname instead of class, e.g.:

<mjx-container classname="MathJax" jax="CHTML">
  <mjx-math classname="MJX-TEX">
    <mjx-mo classname="mjx-n">
      <mjx-c classname="mjx-c3D"></mjx-c>
    </mjx-mo>
    <mjx-mfrac space="4">
      <mjx-frac>
        <mjx-num>
          <mjx-nstrut></mjx-nstrut>
          <mjx-mstyle size="s" style="color: rgb(76, 175, 80);">
            <mjx-mtext classname="mjx-n">
              <mjx-c classname="mjx-c41"></mjx-c>
              <mjx-c classname="mjx-c6E"></mjx-c>
              <mjx-c classname="mjx-c7A"></mjx-c>
              <mjx-c classname="mjx-c61"></mjx-c>
              <mjx-c classname="mjx-c68"></mjx-c>
              <mjx-c classname="mjx-c6C"></mjx-c>
              <mjx-c classname="mjx-c20"></mjx-c>
              <mjx-c classname="mjx-c64"></mjx-c>
              <mjx-c classname="mjx-c65"></mjx-c>
              <mjx-c classname="mjx-c72"></mjx-c>
              <mjx-c classname="mjx-c20"></mjx-c>
              <mjx-c classname="mjx-c45"></mjx-c>
              <mjx-c classname="mjx-c6C"></mjx-c>
              <mjx-c classname="mjx-c65"></mjx-c>
              <mjx-c classname="mjx-c6D"></mjx-c>
              <mjx-c classname="mjx-c65"></mjx-c>
              <mjx-c classname="mjx-c6E"></mjx-c>
              <mjx-c classname="mjx-c74"></mjx-c>
              <mjx-c classname="mjx-c65"></mjx-c>
              <mjx-c classname="mjx-c20"></mjx-c>
              <mjx-c classname="mjx-c69"></mjx-c>
              <mjx-c classname="mjx-c6E"></mjx-c>
              <mjx-c classname="mjx-cA0"></mjx-c>
            </mjx-mtext>
            <mjx-mi classname="mjx-i">
              <mjx-c classname="mjx-c1D43E TEX-I"></mjx-c>
            </mjx-mi>
            <mjx-mo classname="mjx-n">
              <mjx-c classname="mjx-c2229"></mjx-c>
            </mjx-mo>
            <mjx-mi classname="mjx-i">
              <mjx-c classname="mjx-c1D445 TEX-I"></mjx-c>
            </mjx-mi>
          </mjx-mstyle>
        </mjx-num>
        <mjx-dbox>
          <mjx-dtable>
            <mjx-line></mjx-line>
            <mjx-row>
              <mjx-den>
                <mjx-dstrut></mjx-dstrut>
                <mjx-mstyle size="s" style="color: rgb(63, 81, 181);">
                  <mjx-mtext classname="mjx-n">
                    <mjx-c classname="mjx-c41"></mjx-c>
                    <mjx-c classname="mjx-c6E"></mjx-c>
                    <mjx-c classname="mjx-c7A"></mjx-c>
                    <mjx-c classname="mjx-c61"></mjx-c>
                    <mjx-c classname="mjx-c68"></mjx-c>
                    <mjx-c classname="mjx-c6C"></mjx-c>
                    <mjx-c classname="mjx-c20"></mjx-c>
                    <mjx-c classname="mjx-c64"></mjx-c>
                    <mjx-c classname="mjx-c65"></mjx-c>
                    <mjx-c classname="mjx-c72"></mjx-c>
                    <mjx-c classname="mjx-c20"></mjx-c>
                    <mjx-c classname="mjx-c45"></mjx-c>
                    <mjx-c classname="mjx-c6C"></mjx-c>
                    <mjx-c classname="mjx-c65"></mjx-c>
                    <mjx-c classname="mjx-c6D"></mjx-c>
                    <mjx-c classname="mjx-c65"></mjx-c>
                    <mjx-c classname="mjx-c6E"></mjx-c>
                    <mjx-c classname="mjx-c74"></mjx-c>
                    <mjx-c classname="mjx-c65"></mjx-c>
                    <mjx-c classname="mjx-c20"></mjx-c>
                    <mjx-c classname="mjx-c69"></mjx-c>
                    <mjx-c classname="mjx-c6E"></mjx-c>
                    <mjx-c classname="mjx-cA0"></mjx-c>
                  </mjx-mtext>
                  <mjx-mi classname="mjx-i">
                    <mjx-c classname="mjx-c1D445 TEX-I"></mjx-c>
                  </mjx-mi>
                </mjx-mstyle>
              </mjx-den>
            </mjx-row>
          </mjx-dtable>
        </mjx-dbox>
      </mjx-frac>
    </mjx-mfrac>
  </mjx-math>
</mjx-container>

I am not quite sure if this is a bug of rehype-mathjax or mathjax itself. Or does CHTML only work with rehype-sanitize, e.g. see README?

Thanks!

Runtime

Other (please specify in steps to reproduce)

Package manager

yarn 2

OS

Windows

Build and bundle tools

Vite

Thanks for reaching out @FunkMonkey! 👋

Or does CHTML only work with rehype-sanitize

While it is a good idea to use rehype-sanitize, it is not required for this project.

I am not quite sure if this is a bug of rehype-mathjax or mathjax itself.

Given that the SVG renderer works fine, but the CHTML one does not.
Runnable demo: https://codesandbox.io/s/eloquent-moore-kc84ny?file=/src/app.tsx

And that the code between SVG: https://github.com/remarkjs/remark-math/blob/main/packages/rehype-mathjax/svg.js
and CHTML https://github.com/remarkjs/remark-math/blob/main/packages/rehype-mathjax/chtml.js
is nearly identical except which mathjax function they use, and validation for a required option.
I tend to think this is a bug in MathJax itself.

/cc @tani in case you have any additional ideas on what may be happening in the chtml

Also cross linking a recent discussion in react-markdown running into the same issue remarkjs/react-markdown#745

wooorm commented

I think this has to do with React supporting properties (className) on HTML/SVG elements, but not on MathML (or custom, or unknown) elements.
We support className everywhere.

Googling MathML + React gives many bugs, e.g., https://stackoverflow.com/questions/51319758/react-16-html-attributes-with-mathml-tags.

MathML was mostly abandoned for years, but recently got updates and implementations. Hopefully React will add/improve MathML support.

Do you have any updates on how to solve it? I've encountered the issue where the rendered SVG is not inline. I suspect it might be related to the same className issue.
image

Welcome @edward1127! 👋
Did you get a chance to read @wooorm's comment right before yours?
In particular:

MathML was mostly abandoned for years, but recently got updates and implementations. Hopefully React will add/improve MathML support.

We don't have an update on the remark side, because remark isn't causing the issue.
You could file an issue with React (https://github.com/facebook/react/issues) or reach out to one of the React communities (https://react.dev/community) for more specific ideas.
Consider sharing a link to the discussion you start in the react community here, so others can find it and use it as reference.

Thanks for the reply. I just reposted your summary vercel/next.js#54852

@edward1127 that link looks to be the question from #81?
Do you believe this and #81 are the same issue? I tend to think they are separate, one in React in one in Next, but happy to hear other perspectives.

yep, you are right.
Just test it out with React, and everything works fine in react for #81 .

I countered this issue, and I think it can be worked around by making a custom rehype plugin to replace each 'classname' property in the hast with 'class'. However, it may not be an efficient method. But if you really want to solve the issue I think this solution deserves a try.

wooorm commented

I don‘t think that works. Whether it works or not it would break lots of things.

If you really want to solve this issue, see the existing comments on this discussion and solve the root cause, or use a working alternative: do not use the CHTML rendered, or use rehype-katex.