Markdown レンダリングパイプライン:MDX から HTML まで
Markdown レンダリングの課題
ToolsKu のブログとチュートリアルは MDX/Markdown を安全な HTML に変換する必要があります。見た目は単純ですが、実際には次が関わります:
- 構文拡張:GFM テーブル、タスクリスト、取り消し線
- サニタイズ:XSS 防止(信頼できないコンテンツ)
- コードハイライト:着色された
<pre><code>ブロック - 見出しアンカー:目次用の slug 自動生成
- 内部リンク:
/pdf/mergeをビルド時に locale 付きリンクへ
unified パイプライン
MDX/Markdown 原文
↓ remark-parse (Markdown → MDAST)
↓ remark-gfm (GFM 拡張)
↓ remark-rehype (MDAST → HAST)
↓ rehype-slug (見出しに id)
↓ rehype-stringify (HAST → HTML)
HTML 出力
ToolsKu は src/lib/blog/render-mdx.ts でこのパイプラインを実装しています:
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkGfm from 'remark-gfm';
import remarkRehype from 'remark-rehype';
import rehypeSlug from 'rehype-slug';
import rehypeStringify from 'rehype-stringify';
const processor = unified()
.use(remarkParse)
.use(remarkGfm)
.use(remarkRehype, { allowDangerousHtml: false })
.use(rehypeSlug)
.use(rehypeStringify);
Frontmatter の解析
記事メタデータは gray-matter で MDX 先頭から取り出します:
---
title: "記事タイトル"
description: "SEO 説明"
author: "ToolsKu チーム"
---
本文...
import matter from 'gray-matter';
const { data: frontmatter, content } = matter(rawMdx);
// frontmatter.title → "記事タイトル"
// content → 本文 Markdown
Frontmatter は SEO metadata、Open Graph、JSON-LD に使われます。
セキュリティ
生 HTML 注入の禁止
.use(remarkRehype, { allowDangerousHtml: false })
false のとき、Markdown 内の <script> は実行されずテキストとしてエスケープされます。
リンクの安全性
外部リンクには rel="noopener noreferrer" と target="_blank" を付与:
// rehype プラグイン:外部リンクに安全属性を追加
function rehypeExternalLinks() {
return (tree) => {
visit(tree, 'element', (node) => {
if (node.tagName === 'a' && isExternal(node.properties.href)) {
node.properties.target = '_blank';
node.properties.rel = 'noopener noreferrer';
}
});
};
}
多言語コンテンツ管理
ToolsKu のファイル構成:
src/content/blog/my-article/
├── meta.json ← 共通メタ(カテゴリ、タグ、日付)
├── zh-CN.mdx ← 簡体字本文
├── zh-TW.mdx ← 繁体字本文
├── en.mdx ← 英語本文
└── ja.mdx ← 日本語本文
meta.json は言語非依存、*.mdx は各言語の本文と frontmatter。ビルド時に generateStaticParams が slug × locale を列挙して事前レンダーします。
パフォーマンス
ビルド時 vs ランタイム
ToolsKu はビルド時レンダリング(SSG)を採用:
next build → MDX 読み込み → unified → HTML を out/ に出力
ランタイム → 静的 HTML を返すだけ(Markdown 処理ゼロ)
リクエストごとの Markdown 処理と比べ、TTFB は ~200ms から ~50ms へ短縮されます。
コンテンツの増分更新
記事の追加・変更後は next build が必要です。18+ 記事 × 4 言語 = 72 ページで、フルビルドは約 3〜5 分です。
MDX コンポーネントとの関係
現状 ToolsKu はプレーン Markdown(React コンポーネント MDX ではない)を使っています:
- パイプラインが単純でコンテンツ用 React SSR オーバーヘッドなし
- 任意の Markdown エディタで編集可能
- 翻訳はテキストのみで済む
記事内にインタラクティブコンポーネント(live demo など)を入れる場合は @next/mdx + React へ移行できます。
まとめ
Markdown パイプラインはコンテンツサイトの基盤です。remark/rehype は組み合わせ可能な処理チェーン、gray-matter はメタデータ、ビルド時レンダリングは最高の性能を実現します。ToolsKu ブログはこの構成の実践例です。
ブラウザローカルツールを無料で試す →