Next.js 14中useMDXComponents无法替换<img>为Image组件问题
问题:Next.js 14中MDX的img标签无法替换为Next.js Image组件
在Next.js 14项目中使用next-mdx-remote/rsc的compileMDX编译MDX内容时,配置useMDXComponents自定义组件后,h2标签能成功添加font-bold类,但img标签始终无法替换为Next.js的Image组件(即使测试替换为hr也无效)。
相关代码及文件内容
useMDXComponents配置代码
import Image, { ImageProps } from 'next/image'; import type { MDXComponents } from 'mdx/types'; export function useMDXComponents(components: MDXComponents): MDXComponents { return { h2: ({ children }) => <h2 className="font-bold">{children}</h2>, img: (props) => <Image sizes="100vw" {...(props as ImageProps)} />, // img: (props) => <hr />, 测试用:尝试将img替换为hr ...components, }; }
MDX内容
Lorem ipsum dolor sit amet, consectetur adipiscing elit. ## This is my heading 2 <img src="/path/to/my/image.png" width="300" height="300" />
渲染后的HTML
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p> <h2 class="font-bold">This is my heading 2</h2> <img src="/path/to/my/image.png" width="300" height="300" />
[...slug]/page.tsx配置代码
import { compileMDX } from 'next-mdx-remote/rsc'; import rehypeSlug from 'rehype-slug'; import remarkGfm from 'remark-gfm'; import fs from 'fs'; import path from 'path'; type Params = { params: { slug: string[] } }; const contentSource = 'path/to/your/content'; // 替换为实际内容目录 export default async function ArticlesPage({ params }: Params) { // 读取MDX文件 const source = fs.readFileSync( path.join(process.cwd(), contentSource, `${params.slug.join('/')}.mdx`), 'utf8' ); // 获取自定义组件 const components = useMDXComponents({}); // 编译MDX内容 const { content, frontmatter } = await compileMDX({ source, options: { mdxOptions: { // rehypePlugins: [rehypeSlug], // remarkPlugins: [remarkGfm], }, parseFrontmatter: true, }, components, }); const pageTitle = frontmatter.title as string; const pageLastUpdate = frontmatter.lastUpdate as string; return ( <> <h1>{pageTitle}</h1> <p className="text-sm italic">Last update: {pageLastUpdate}</p> <div>{content}</div> </> ); }
问题原因
MDX对原生HTML标签(如<img>)和Markdown语法生成的组件(如##对应的h2)处理逻辑不同:
- Markdown的
##会被解析为MDX标准组件,因此能匹配useMDXComponents中的h2键; - 原生HTML的
<img>会被当作原始HTML节点直接渲染,不会映射到自定义组件中的img键。
解决方法
方法1:改用MDX支持的图片语法
将MDX中的原生<img>标签替换为Markdown图片语法,这样MDX会将其解析为标准img组件,从而触发自定义映射:
Lorem ipsum dolor sit amet, consectetur adipiscing elit. ## This is my heading 2 
方法2:用rehype插件转换原生HTML标签
通过rehype-react插件将原生HTML标签转为MDX可识别的组件,步骤如下:
- 安装依赖:
npm install rehype-react
- 修改
compileMDX的配置,添加插件并映射img组件:
import rehypeReact from 'rehype-react'; import { Fragment, jsx } from 'react/jsx-runtime'; // ... 其他代码 const { content, frontmatter } = await compileMDX({ source, options: { mdxOptions: { rehypePlugins: [ [rehypeReact, { createElement: jsx, Fragment, components: { img: (props) => <Image sizes="100vw" {...props} />, }, }], // rehypeSlug, ], // remarkPlugins: [remarkGfm], }, parseFrontmatter: true, }, components: useMDXComponents({}), });
额外注意事项
- 使用Next.js Image组件时,本地图片需放在
public目录下,远程图片需在next.config.js中配置images.remotePatterns; - 确保
useMDXComponents函数被正确导入到page.tsx中。
内容的提问来源于stack exchange,提问作者Mohsen Taleb
相关产品推荐
相关产品推荐

