Next.jsのMDXでMarkdown形式のテーブルを表示する(remark-gfm + next-mdx-remote)

問題:MDXでテーブルが文字列として表示される

Next.jsプロジェクトでMDXファイル内にMarkdown形式のテーブルを書いたところ、テーブルとして表示されず文字列のまま表示されてしまった。

| column_name | is_identity | identity_generation | column_default                      |
| ----------- | ----------- | ------------------- | ----------------------------------- |
| onidazo_id  | NO          | null                | nextval('onidazo_id_seq'::regclass') |

この記事では、このようなMarkdown形式のテーブル(パイプテーブル)を正しく<table>要素としてレンダリングできるようにした実装手順を紹介する。

環境

原因:GitHub Flavored Markdownのサポートが必要だった

調べてみると、Markdown形式のテーブルは GitHub Flavored Markdown (GFM) の拡張構文であり、標準のMarkdownパーサーではサポートされていないことが分かった。そのため、remark-gfmプラグインを追加する必要があった。

remarkとrehypeの違い

テーブル構文の解析はMarkdownレベルで行われるため、remarkプラグインremark-gfm)を使用した。

なお、このプロジェクトでは既にコードブロックのシンタックスハイライトのためにrehype-pretty-codeshikirehypeプラグインとして使用していたが、remarkとrehypeは処理のタイミングが異なるため、両方を併用できた。

実装手順

1. remark-gfmパッケージをインストール

まず、remark-gfmパッケージをインストールした。

yarn add remark-gfm

2. mdxOptions に remarkPlugins を追加

今回のプロジェクトではnext-mdx-remotecompileMDXを使ってMDXをレンダリングしていたので、mdxOptionsremarkPluginsを追加した。

既にrehype-pretty-codeshikiを使用していたため、既存のrehypePluginsはそのままで、remarkPlugins配列にremarkGfmを追加した。

// lib/note.ts
import { compileMDX } from "next-mdx-remote/rsc";
import rehypePrettyCode from "rehype-pretty-code";
import remarkGfm from "remark-gfm";
 
const { content, frontmatter } = await compileMDX({
  source: fileContent,
  options: {
    parseFrontmatter: true,
    mdxOptions: {
      remarkPlugins: [remarkGfm],  // ← remarkプラグインとして追加
      rehypePlugins: [
        // 既存のrehypeプラグイン(そのまま残す)
        [
          rehypePrettyCode,
          {
            theme: {
              light: "vitesse-light",
              dark: "vitesse-dark",
            },
          },
        ],
      ],
    },
  },
  components: {
    // カスタムコンポーネント
  },
});

3. テーブルのスタイリング(オプション)

次に、テーブルにカスタムスタイルを適用するため、MDXコンポーネントをカスタマイズした。

// app/mdx-components.tsx
import type { MDXComponents } from "mdx/types";
 
const components: MDXComponents = {
  table: ({ children }) => (
    <div>
      <table>
        {children}
      </table>
    </div>
  ),
  thead: ({ children }) => (
    <thead>{children}</thead>
  ),
  tbody: ({ children }) => (
    <tbody>{children}</tbody>
  ),
  tr: ({ children }) => (
    <tr>{children}</tr>
  ),
  th: ({ children }) => (
    <th>
      {children}
    </th>
  ),
  td: ({ children }) => (
    <td>{children}</td>
  ),
};
 
export function useMDXComponents(): MDXComponents {
  return components;
}

このカスタムコンポーネントをcompileMDXcomponentsに追加した。

// lib/note.ts
import { useMDXComponents } from "@/app/mdx-components";
 
const { content, frontmatter } = await compileMDX({
  source: fileContent,
  options: {
    // ...
  },
  components: {
    ...useMDXComponents(),  // ← カスタムコンポーネントを展開
    // その他のコンポーネント
  },
});

動作確認

開発サーバーを再起動して動作確認した。

yarn dev

重要: Next.jsの設定ファイル(next.config.mjslib/note.ts)を変更した場合は、必ず再起動が必要である。

トラブルシューティング

テーブルが正しく表示されない場合は、以下を試すと良い。

  1. 開発サーバーを完全に停止して再起動
  2. ブラウザのキャッシュをクリア(ハードリロード: Cmd+Shift+R)
  3. .nextフォルダを削除して再ビルド
rm -rf .next
yarn dev

結果

これで、Markdown形式のテーブルが正しくHTMLの<table>要素としてレンダリングされるようになった。

column_nameis_identityidentity_generationcolumn_default
onidazo_idNOnullnextval('onidazo_id_seq'::regclass')

remark-gfmで使えるその他の機能

remark-gfmを追加したことで、テーブル以外にも以下のGitHub Flavored Markdown機能が使えるようになった。

取り消し線

~~text~~で取り消し線を表示できる。

タスクリスト

- [ ]- [x]でチェックボックス付きリストを表示できる。

自動リンク

URLを書くだけで自動的にリンクになる。

脚注

[^1]のような記法で脚注を追加できる。1

まとめ

参考リンク

  1. これは脚注の例である。脚注は自動的にページ下部に表示される。