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

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

![图片描述文本](/path/to/my/image.png)

方法2:用rehype插件转换原生HTML标签

通过rehype-react插件将原生HTML标签转为MDX可识别的组件,步骤如下:

  1. 安装依赖:
npm install rehype-react
  1. 修改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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 05:44:54