こんにちは、エンジニアのオオバです。

MDXから出力したHTMLの加工ができない!!
そういう悩みありませんか?

目次が作りたくて、
以下のように出力したい場合があります。

Before(通常のMDX)
<h2>あああああああああ</h2>  
<h3>いいいいいいいいい</h3>  

After(やりたいこと)
<h2 id = "あああああああああ">あああああああああ</h2>  
<h3 id = "いいいいいいいいい">いいいいいいいいい</h3>  

本記事ではMDXのカンタンな
整形方法をわかりやすく解説していきます。

→11万文字で徹底解説した「DOTweenの教科書」Unityアニメーションの超効率化ツールはこちら

本記事で解決すること

MDXProviderにMDX整形ルールを渡すだけ

最初に結論です。
MDXProviderにカスタムしたコンポーネントを渡すことで
MarkDownからMDXへの整形をカスタマイズできます。

  1. Appコンポーネント
  2. MDX整形ルールの定義

手順1.Appコンポーネントの修正

_app.jsx(.tsx)のコンポーネントの修正内容です。
引数のpagePropsをMDXProviderで囲みます。

具体的には以下のコードです。

💻ソースコード : _app.jsxの抜粋
<MDXProvider components={mdComponents}>  
    <Component { ...pageProps } />  
</MDXProvider>  

このままではmdComponents
存在しないエラーが出てしまいますので、
mdComponentsについて解説します。

手順2.MDX整形ルールの定義

ポイントはココ{mdComponents}です。
htmlタグ要素に対して、整形ルールを
オブジェクト型で定義します。

具体的には以下のコードです。

💻ソースコード : _app.jsxの抜粋
const mdComponents = {  
  h1: (props) => { return (<h1 id={props.children}>{props.children}</h1>)},  
  h2: (props) => { return (<h2 id={props.children}>{props.children}</h2>)},  
  h3: (props) => { return (<h3 id={props.children}>{props.children}</h3>)},  
}

h1h3のタグの時の加工処理を記述してます。

### hogehoge
<h3 id="hogehoge">hogehoge</h3>  

と整形されます。

まとめ

本記事では追加ライブラリなしで、
MDXのHTMLタグをカスタマイズしてみました。

手順をまとめると以下の3つです。

  1. 整形ルールを作成
  2. MDXProviderで囲む
  3. MDXProviderに整形ルールを渡す

コード全体はコチラ

💻ソースコード : _app.jsxの抜粋
import { MDXProvider } from "@mdx-js/react"
// ~~~ 省略 ~~~  

// MDX整形ルール  
const mdComponents = {  
  h1: (props) => { return (<h1 id={props.children}>{props.children}</h1>)},  
  h2: (props) => { return (<h2 id={props.children}>{props.children}</h2>)},  
  h3: (props) => { return (<h3 id={props.children}>{props.children}</h3>)},  
}

const MyApp = ({ Component, pageProps }) => {  
    // ~~~ 省略 ~~~  
    return (  
    <>  
    // ~~~ 省略 ~~~  
    <MDXProvider components={mdComponents}>  
        <Component { ...pageProps } />  
    </MDXProvider>  
    // ~~~ 省略 ~~~  
    </>  
    )  
}
export default MyApp  

ERR_REQUIRE_ESMでremark-slugは使えなかった

最初remark-slugというプラグインを検討しましたが、
ERR_REQUIRE_ESMというエラーで断念。

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module:  

以下のようにnext.config.jsに
設定するだけでは済みませんでした。

💻ソースコード : next.config.jsの抜粋
const withMDX = require('@next/mdx')({  
  extension: /\.(md|mdx)?$/,  
  options: {  
    remarkPlugins: [require('remark-slug')],  
  },  
})  
const nextConfig = {  
    // ~~~省略~~~  
}
module.exports = withMDX(nextConfig)  

remark-slugを使う方法は、
キーワード "Next.js 目次作成" で検索すると
ヒットします。

執筆時点ではうまく動かなかったことを共有しておきます。

オススメ記事
検証環境