You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js 13(App Router)服务端集成MathJax渲染LaTeX公式问题

解决Next.js 13 App Router中MathJax错误渲染所有文本的问题

核心原因

问题出在MathJax的配置上——未正确指定公式分隔符,导致它将所有文本误判为LaTeX公式。同时需要适配App Router的SSR逻辑,避免客户端重渲染跳动。


解决方案步骤

1. 正确配置MathJax的公式识别规则

确保MathJax只处理$(行内)和$$(块级)包裹的内容,同时忽略普通美元符号和非公式文本:

MathJax = {
  tex: {
    inlineMath: [['$', '$'], ['\\(', '\\)']], // 仅识别$包裹的行内公式
    displayMath: [['$$', '$$'], ['\\[', '\\]']], // 仅识别$$包裹的块级公式
    processEscapes: true, // 允许用\$表示普通美元符号
    processEnvironments: true
  },
  options: {
    skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'], // 跳过这些标签内的内容
    ignoreHtmlClass: 'tex2jax_ignore' // 带该类的元素不处理
  }
};

2. App Router下的SSR实现方案

根据内容是否动态,选择两种实现方式:

方案A:纯服务端渲染静态内容

创建服务端组件MathContent.tsx:

export default function MathContent({ content }: { content: string }) {
  return (
    <>
      <div className="math-content">{content}</div>
      {/* 加载MathJax脚本并注入配置 */}
      <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
      <script dangerouslySetInnerHTML={{
        __html: `
          MathJax = {
            tex: {
              inlineMath: [['$', '$'], ['\\(', '\\)']],
              displayMath: [['$$', '$$'], ['\\[', '\\]']],
              processEscapes: true
            },
            options: {
              skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'],
              ignoreHtmlClass: 'tex2jax_ignore'
            }
          };
        `
      }} />
    </>
  );
}

在页面中使用(如app/page.tsx):

import MathContent from './MathContent';

export default function Home() {
  const content = `
    这是行内公式:$x^2-2x+5=0$,普通文本不会被渲染。
    这是块级公式:
    $$x^2-2x+5=0$$
    普通美元符号示例:\\$100(用\\$转义避免被识别为公式)
  `;

  return (
    <main>
      <MathContent content={content} />
    </main>
  );
}
方案B:客户端组件处理动态内容

如果内容需要动态更新,创建客户端组件MathRenderer.tsx(添加'use client'指令):

'use client';

import { useEffect, useRef } from 'react';

export default function MathRenderer({ content }: { content: string }) {
  const contentRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (contentRef.current && window.MathJax) {
      // 内容更新时重新渲染公式
      window.MathJax.typesetPromise([contentRef.current]).catch(err => console.error(err));
    }
  }, [content]);

  return <div ref={contentRef} dangerouslySetInnerHTML={{ __html: content }} />;
}

在根布局app/layout.tsx中全局加载MathJax配置和脚本:

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="zh-CN">
      <body>
        {children}
        <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
        <script dangerouslySetInnerHTML={{
          __html: `
            MathJax = {
              tex: {
                inlineMath: [['$', '$'], ['\\(', '\\)']],
                displayMath: [['$$', '$$'], ['\\[', '\\]']],
                processEscapes: true
              },
              options: {
                skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'],
                ignoreHtmlClass: 'tex2jax_ignore'
              }
            };
          `
        }} />
      </body>
    </html>
  );
}

然后在页面中引入客户端组件:

import MathRenderer from './MathRenderer';

export default function Home() {
  const content = `
    动态行内公式:$x^2-2x+5=0$
    动态块级公式:
    $$x^2-2x+5=0$$
  `;

  return (
    <main>
      <MathRenderer content={content} />
    </main>
  );
}

关键注意事项

  • processEscapes: true:必须开启,否则普通美元符号会被误判为公式分隔符。
  • 避免重复加载MathJax脚本:建议在根布局中统一加载,不要在多个组件中重复引入。
  • SSR视觉优化:如果想完全避免客户端渲染的跳动,可以使用mathjax-node工具在服务端预渲染公式为HTML,但需要额外的后端处理逻辑。

内容的提问来源于stack exchange,提问作者Sudarmono Mono

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 02:55:17