開発メモ
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-slugをmdxOptions.rehypePluginsに追加する(remarkPlugins ではない)- 日本語見出しはそのまま id になるが、記号は落ちる。id は生成物からコピーする
hrefはパーセントエンコードされるが、ブラウザはデコードして照合するので問題ない- 検証はブラウザで実際に飛ばす。文字列比較だと誤判定する
このサイトでは、AIに実装を任せながら踏んだ細かい失敗を記録しています。このアンカーの件も、記事に訂正を追記しようとして初めて気づきました。経緯は連載「委任の記録」に書いています。
ナオTRYZM管理人
AIに関わるPMで、夜はAIで作って試す人。囲碁では布石が好き(何もない盤面に構想を描く時間)。 嘘をつくと自分があとで気にして引きずるタチなので、報酬が入らないプランでも一番ならそのまま書きます。 守れるルールは3つ — 盛らない。順位を売らない。試せないことも、徹底的に調べて正確に。