- - MDXを使うメリット
- - Next.js + MDXを使ってできなかったこと
- - MDXの変換を自分で呼び出すように
- - MDXファイルの動的埋め込み
- - 解決方法1: Next.js + MDX Enhancedが選択肢になる
- - 解決方法2: Next MDX Remoteが良さそう
- - 結果
目次
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 ...
という文字が表示されるという哀れな結果でした。
そもそもgetStaticProps
はnext 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を使って無事埋め込むことができました!