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
相关产品推荐
相关产品推荐

