Home:0
きままな個人開発ブログ
Next.jsでブログを書く際にMDXを利用するならNext MDX Remoteがとても柔軟だった
公式のMDXライブラリでは色々制限があったので色々試した結果をまとめました。

MDXを使うメリット

このブログもNext.jsによりMarkdownで記載しています。YouTubeやTwitterなどを記事本文に埋め込みたい時を考えるとMDXを使いたいところです。しかし、MDXファイル内はTypescriptの型チェックがサポートされていません。issueもありましたが対応してくれなさそうです。今後過去記事のメンテを考えると気軽にReactのコンポーネントを埋め込む選択肢はなくなりました。記事が増えてきた時のことも考え汎用的なコンポーネントを一つ作りその中で分岐しようと思いました。

markdownファイルのイメージ

## 記事見出し  
<EmbedComponent options={{ kind: 'youtube', url: 'https...' }} />  
記事本文...  

Next.js + MDXを使ってできなかったこと

Next.jsの公式には、Next.js + MDXが記載されていたので使ってみました。

pagesディレクトリに配置した.mdxファイルがそのままURLになってとても簡単に導入できるのですが、ブログのヘッダやサイドバーなどのlayoutファイルを全部(ここではブログの1記事分)の.mdxファイル内に記載する必要がありました。

これらのコンポーネントは埋め込みたくないですし、目次の動的な生成もやりたかったので試行錯誤しました。

MDXの変換を自分で呼び出すように

Next.jsではgetStaticPropsが提供されていて、その関数内ではnode.jsが利用できるため、fsによるファイルの読み書きが可能になります。チュートリアルでは通常のmarkdownファイルをHTMLに変換し本文用のページへ埋め込み表示しています。

そこでMDX async-apiconst jsx = await mdx(content)を使ってgetStaticProps内で変換しました。JSX形式は戻り値として扱えずJSON.stringifyで文字列に変換したものの実際表示する際にhtmlとして表示していたため<EmbedComponent ...という文字が表示されるという哀れな結果でした。

そもそもgetStaticPropsnext build時に結果をjsonファイルとして書き出すもので、ページ遷移時にはそのファイルを読み込んでくれ表示されます。仮にこの方法で表示させようとしたらEmbedComponentはどこで importするのでしょうか。完全に迷走しました。

MDXファイルの動的埋め込み

同様なパターンで記載するには @mdx-js/runtime を使えば良さそうです。下記を見る限り必要なコンポーネントやパラメータを渡しているので実現できそうです。

<MDX components={components}  scope={scope}>
  {mdx} // mdxファイルの中身
</MDX>

しかしながらこういう記載がありました。

⚠️ warning: this is not the preferred way to use MDX since it introduces a substantial amount of overhead and dramatically increases bundle sizes. It should also not be used with user input that isn’t sandboxed.

オーバーヘッドとバンドルサイズが大きくなるとのこと。せっかくSSGで軽量なサイトを作ろうとしているのに本末転倒とはこのことですね。

解決方法1: Next.js + MDX Enhancedが選択肢になる

同じように困っている方がいらっしゃいました。Dynamically import components depending on getStaticProps response

最後の回答には自分が上記検証した内容ができない説明とNext.js + MDX Enhancedが紹介されていました。

  • getStaticProps内でFrontmatterを使った一覧ページを作成できる
  • layoutsフォルダに配置したコンポーネントで共通レイアウトが作れる
  • ページ毎にレイアウトも可能だがパスでレイアウトファイルを判断してくれる

これで実現しても良さそうですがすでに一覧ページなどは作成しているのと、今後MDXファイルをホスティングした場合これでは難しそうです。

解決方法2: Next MDX Remoteが良さそう

Next MDX Remoteはちょうどホスティング利用を想定しているライブラリでした。上記MDX Enhancedのビルド問題も指摘していてコンテンツが増えた際にはWebpackにやらせるべきではない旨が記載されていました。

You will end up running into performance issues. Webpack is a javascript bundler, forcing it to load hundreds/thousands of pages of text content will blow out your memory requirements - webpack stores each page as a distinct object with a large amount of metadata. One of our implementations with a couple hundred pages hit more than 8gb of memory required to compile the site. Builds took more than 25 minutes.

結果

Next MDX Remoteを使って無事埋め込むことができました!