Markdown レンダリングパイプライン:MDX から HTML まで

技术架构(更新: 2026年5月29日)

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 ブログはこの構成の実践例です。

ブラウザローカルツールを無料で試す →

#Markdown#MDX#remark#rehype#内容渲染