TRYZM
← ブログ一覧

開発メモ

next-mdx-remote で見出しに id が付かず、記事内アンカーリンクが効かない(rehype-slug)

2026-08-06

MDXで書いた記事の中に、こういうリンクを置いたとします。

詳しくは下の [訂正](#訂正) を読んでください。

## 訂正

クリックしても飛びません。

原因: 見出しに id が生成されていない

出力されたHTMLを見ると、こうなっています。

<h2>訂正</h2>

id がありません。アンカーリンクは id を探して飛ぶので、id が無ければ何も起きません。

MDX(remark/rehype)は、既定では見出しに id を付けません。 GitHubやZennなどが自動で付けてくれるので、同じ挙動を期待して踏みます。

確認はこれで済みます。

curl -s http://localhost:3000/blog/your-post | grep -oE '<h2[^>]*>' | head
# → <h2>  … id が無ければこれ

直し方: rehype-slug を入れる

npm install rehype-slug

next-mdx-remote/rsc を使っている場合、options.mdxOptions.rehypePlugins に渡します。

import { MDXRemote } from "next-mdx-remote/rsc";
import rehypeSlug from "rehype-slug";

<MDXRemote
  source={post.content}
  options={{
    mdxOptions: {
      rehypePlugins: [rehypeSlug],
    },
  }}
/>

これで見出しに id が付きます。

<h2 id="訂正">訂正</h2>

remark-gfm(表組みなど)と併用する場合は、こう分かれます。

options={{
  mdxOptions: {
    remarkPlugins: [remarkGfm],   // markdown側
    rehypePlugins: [rehypeSlug],  // HTML側
  },
}}

remark と rehype は別の枠です。rehype-slug を remarkPlugins に入れても効きません。

日本語の見出しはどうなるか

rehype-slug は github-slugger を使っていて、日本語はそのまま id になります(記号と空白は落ちます)。

見出し生成される id
## 訂正訂正
## 訂正:殺したのは301ではなく、その前の404だった訂正殺したのは301ではなくその前の404だった

などの記号が消えるので、リンク側もそれに合わせる必要があります。手で書くとまず間違えるので、生成されたHTMLから id をコピーするのが確実です。

なお、Markdown側でリンクを書くと hrefパーセントエンコードされます。

<a href="#%E8%A8%82%E6%AD%A3">訂正</a>
<h2 id="訂正">訂正</h2>

見た目は一致していませんが、ブラウザはフラグメントをデコードしてから照合するので、これで正しく飛びます。文字列比較で検証すると「不一致」に見えるので注意してください(私はこれで一度「壊れている」と誤判定しました)。

確認方法

実際に飛ぶかどうかは、ブラウザで確かめるのが確実です。

// 開発者ツールのコンソールで
[...document.querySelectorAll('a[href^="#"]')].map(a => {
  const id = decodeURIComponent(a.getAttribute('href').slice(1));
  return { text: a.textContent.trim(), ok: !!document.getElementById(id) };
});

ok: false があれば、その id は存在していません。

まとめ

  • MDXは既定では見出しに id を付けない。アンカーリンクは黙って効かなくなる
  • rehype-slugmdxOptions.rehypePlugins に追加する(remarkPlugins ではない)
  • 日本語見出しはそのまま id になるが、記号は落ちる。id は生成物からコピーする
  • href はパーセントエンコードされるが、ブラウザはデコードして照合するので問題ない
  • 検証はブラウザで実際に飛ばす。文字列比較だと誤判定する

このサイトでは、AIに実装を任せながら踏んだ細かい失敗を記録しています。このアンカーの件も、記事に訂正を追記しようとして初めて気づきました。経緯は連載「委任の記録」に書いています。

ナオTRYZM管理人

AIに関わるPMで、夜はAIで作って試す人。囲碁では布石が好き(何もない盤面に構想を描く時間)。 嘘をつくと自分があとで気にして引きずるタチなので、報酬が入らないプランでも一番ならそのまま書きます。 守れるルールは3つ — 盛らない。順位を売らない。試せないことも、徹底的に調べて正確に。