マークダウンプレビュー

Heading 1


Heading 2


Heading 3

This is a paragraph with bold and italic text.

  • List item 1

  • List item 2

  • List item 3

  • Numbered item 1

  • Numbered item 2

inline code

const hello = "world";
console.log(hello);

Blockquote text

Link text


| Column 1 | Column 2 |
|----------|----------|
| Cell 1 | Cell 2 |

CommonMark と GitHub Flavored Markdown:実際に何が違うのか

Markdown は 2004 年の Perl スクリプトと緩く書かれた文法ページから始まったため、10 年にわたり実装ごとにエッジケースの解釈が食い違っていました。CommonMark(2014)がこれを解決しました。650 以上の番号付き例がそのまま適合性テストスイートとして機能する、曖昧さのない仕様です。今日、2 つのレンダラーの出力が異なる場合は CommonMark 仕様が最終判定になります。 GitHub Flavored Markdown(GFM)は CommonMark の正式なスーパーセットです。追加される拡張はちょうど 5 つ — テーブル、タスクリスト項目、チルダ 2 つによる打ち消し線、素の URL の自動リンク、そして危険な raw HTML タグ(script、title、iframe、style など)を除去する tagfilter です。GitHub 上で見かけるそれ以外の機能 — [!NOTE] のようなアラート引用、Mermaid 図、脚注、絵文字ショートコード — は GFM 仕様ではなく GitHub 製品側の挙動であり、npm、GitLab、多くの静的サイトジェネレーターでは描画されません。 実務で刺さる違い:CommonMark は山括弧で囲んだ URL しか自動リンクしませんが、GFM は www. や https:// で始まる素のテキストもリンク化します。GFM のテーブルはヘッダー行の下にダッシュの区切り行が必須で、セル内のパイプ文字はバックスラッシュでエスケープが必要です。タスクリストのチェックボックスはリスト項目の先頭にある場合のみ機能します。複数プラットフォームで同一に描画すべき文書なら、CommonMark コアの範囲に留まり、拡張機能の使用は移植性に関する明示的な判断として扱ってください。
GFM extensions (will NOT render in strict CommonMark):

| Flag | Meaning |
|------|---------|
| -v   | verbose |

- [x] ship parser
- [ ] write docs

~~deprecated~~   www.example.com   <- GFM autolinks bare URLs

CommonMark-portable equivalents:
<https://www.example.com>          <- autolink works everywhere
Cell with \| escaped pipe          <- required inside GFM tables

強調・リスト・改行:全員がつまずくルール

強調記法は簡単そうに見えて、実は CommonMark 仕様で最も複雑な部分です — delimiter-run のルールだけで数ページあります。実務上のポイント:アンダースコアは単語内部では強調を作らないため snake_case_names はそのまま残りますが、アスタリスクは作ります(a*b*c は b を強調)。アスタリスク 3 つは外側から内側へ太字と斜体を入れ子にします。強調が効かないときの原因はほぼ必ず、区切り文字の間違った側にある空白です — 開きアスタリスクの直後に空白があると left-flanking にならず、文字のまま残ります。 リストには有名な罠が 2 つあります。第一に、インデントは相対的です。入れ子の項目は固定の 4 スペースではなく、親の最初のコンテンツ列に揃える必要があります。親のマーカーが「1. 」なら子は 3 列目、「10. 」なら 4 列目です。第二に、loose と tight:どこか 2 項目の間に空行が 1 つでもあると全項目が段落タグで包まれ、縦の間隔が目に見えて広がります —「リストの行間がバラバラに見える」問題の最頻原因です。 順序付きリストは最初の番号だけを尊重します。7、8、9 と書くと 7 から始まるリストになり、以降の番号は無視されます。行頭の西暦にピリオドが続くと(「2026. その年は…」)静かに順序付きリストへ変わります — ピリオドをバックスラッシュでエスケープしてください。 改行:単一の改行は soft break で、ファイルでは空白として描画されます。強制改行(hard break)には行末にスペース 2 つかバックスラッシュを置きます。GitHub のコメントと Issue は単一改行を hard break として描画しますが、README ファイルはそうしません — 同じ記法で出力が違う、混乱の永久機関です。
snake_case_name          -> no emphasis (intraword underscore)
a*b*c                    -> a<em>b</em>c (asterisk works intraword)
* not italic *           -> literal (space after opening delimiter)

7. seven
8. eight                 -> renders as a list starting at 7

2026\. was a good year   -> escaped: stays a paragraph

line one··               (two trailing spaces = hard break)
line two\
line three               (backslash = hard break, survives autoformat)

正規表現ベースの Markdown 変換が壊れる理由(本物のパーサーは何をしているか)

文字列置換を連鎖させれば見出しや太字くらいは変換できますが、Markdown は正規言語ではありません — 構文要素は任意に入れ子になります。コードスパン内のアスタリスクは強調になってはならず、フェンス付きコードブロック内の # で始まる行は見出しになってはならず、引用の中にリスト、その中にまた引用が入り得ます。正規表現パイプラインはこれらを固定順で処理するため、必ずどれかの構文が別の構文を壊します。手早いプレビューには許容範囲でも、公開物には危険です。 仕様準拠のパーサーは、CommonMark 仕様のパース戦略付録に記載のとおり 2 段階で動作します。まず文書をブロックの木(段落・リスト・引用・コードフェンス)に分割し、その後インライン解析を許可されたブロック内でのみ実行します。コードフェンスの中に Markdown 風のテキストを安全に書ける理由はまさにこれで、インライン規則がそこへ入り込まないからです。 JavaScript の主流の選択肢:markdown-it(高速・プラグイン豊富・CommonMark 準拠)、micromark + remark(最小の正確なコアと AST エコシステム、MDX のエンジン)、marked(最古で最速だが歴史的にエッジケースに緩い)。C のリファレンス実装 cmark は毎秒数メガバイトを解析し、仕様テストはこれを基準に作られています。 何を選ぶにせよ、一度は仕様のサンプルスイートで検証してください。自称 Markdown パーサーの少なくない数が 650 超の適合性ケースのうち数十件で失敗します — そしてその失敗箇所こそ、ユーザーがいつか入力するエッジケースなのです。

Raw HTML と XSS:描画する前にサニタイズを

Markdown は設計上 raw HTML をそのまま通します — そしてその柔軟さこそ、ユーザー提供の Markdown を描画する際の最大のセキュリティリスクです。onerror 属性付きの img タグは JavaScript を実行します。完全に正しい Markdown リンクが javascript: や data: URL を指すこともできます。HTML ブロックすら不要で、インライン構文だけで十分成立します。 レンダラーの HTML 出力は信頼しないでください。防御は 3 層です。第一に、不要なら パーサーで raw HTML を無効化する(markdown-it なら html を false に。remark 系なら rehype-raw を追加しない)。第二に、リンクと画像の宛先を http・https・mailto のスキーム許可リストに制限する — パーサーは javascript: 宛先も構文的に正しい Markdown と見なします。第三に、最終 HTML を DOMPurify のような本物のサニタイザーに通す。自作のタグ除去正規表現は、入れ子や不正な形のタグで確実にバイパスされます。 GitHub 自身のパイプラインが良い手本です。GFM を描画し、tagfilter 拡張を適用し(script・iframe・style などの危険タグを除去)、属性許可リスト型サニタイザーを通してからページに到達させます。順序に注意 — サニタイズは描画の後です。Markdown 記法自体が、ソースには見えなかった HTML を生成し得るからです。 最後の罠:信頼できない Markdown を自分のブラウザでプレビューすると、含まれる HTML が実行されます。サンドボックスは公開後の出力だけでなく、プレビュー環境にも必要です。
<!-- all of these are "valid Markdown" from a parser's view -->
<img src=x onerror=alert(document.cookie)>
[click me](javascript:alert(1))
[harmless](data:text/html;base64,PHNjcmlwdD4uLi48L3NjcmlwdD4=)

// safe rendering pipeline
const md = markdownit({ html: false, linkify: true });
const dirty = md.render(userInput);
const clean = DOMPurify.sanitize(dirty, {
  ALLOWED_URI_REGEXP: /^(https?|mailto):/i
});
container.innerHTML = clean;

ポータブルな Markdown を書く:1 つのソース、複数のレンダラー

同じ .md ファイルが GitHub(GFM)、静的サイトジェネレーター、npm のレジストリページ、IDE のプレビュー、pandoc で描画されることがあり — それぞれエンジンが違います。Hugo は Goldmark(CommonMark 準拠)、Jekyll のデフォルトは kramdown で、これは CommonMark ではなく波括弧の属性リストなど独自記法を持ちます。pandoc にはオプトインの拡張が数十個あります。トリプルダッシュに挟まれた YAML フロントマターはそもそも Markdown ではありません — 想定しないレンダラーはページ先頭にテーブルや生テキストとして表示してしまいます。 割に合う移植性ルール:Setext の下線方式より ATX 見出し(# 記号)を使う。4 スペースインデントではなく、言語タグを明示したフェンス付きコードブロックを常に使う — インデント式コードは言語を表現できず、インデントが曖昧なためリストと悲惨に干渉します。すべてのブロック要素(リスト・テーブル・フェンス)の前後に空行を置く。lazy continuation の規則がエンジンごとに違うからです。行末スペース 2 つの hard break には頼らない — エディタやフォーマッタが保存時に平気で削除します。代わりにバックスラッシュを。 寿命の長い文書なら 1 文 1 行(one-sentence-per-line)を採用する価値があります。git diff を文単位でレビューでき、soft break は空白に畳まれるため描画結果は同一で、単語 1 つの変更で段落全体が再折り返しされて blame 履歴を汚す問題も避けられます。
---
title: parsed as front matter by Hugo/Jekyll,
       printed as text (or a table!) by others
---

Portable                     Fragile
--------                     -------
# ATX heading                Setext heading underlined with ===
fenced block + language      4-space indented code (no language)
break with backslash \       break with two trailing spaces
blank line before a list     list glued to the previous paragraph
最終更新:

ツールについて

Markdown プレビューワーは CommonMark / GFM をリアルタイムで HTML に変換します。README・ブログ・ドキュメントが公開前にどう見えるかを即確認でき、書き直しの往復を減らせます。

使い方

  1. Type or paste Markdown into the editor on the left.
  2. The preview pane on the right renders updates instantly.
  3. Use standard syntax: # headings, **bold**, `code`, ``` fenced blocks ```, [links](#).
  4. Click Copy HTML to grab the rendered output for use in CMS or email.
  5. Iterate until the layout looks right, then commit the Markdown source to your repo.

主な使用例

  • Drafting a README before pushing to GitHub or GitLab.
  • Previewing a blog post for a static site generator like Hugo or Astro.
  • Composing a release note that will be pasted into GitHub Releases.
  • Checking a Pull Request description renders correctly.
  • Producing HTML email content from a Markdown source for a newsletter.
  • Verifying a documentation page before opening a Docs PR.

よくある質問

Q. Does this support GitHub-flavored extensions like task lists?

A. Yes — checkboxes, fenced code blocks, autolinks, and tables work. Some platform-specific features (alerts, mermaid diagrams) may render differently on GitHub itself.

Q. Why does my code block lose syntax highlighting?

A. The previewer renders structure, not full syntax themes. GitHub, Gitea, and most static site generators apply their own highlighting on top.

Q. Can I render math (LaTeX)?

A. Not in the basic preview. Some Markdown processors integrate KaTeX or MathJax — check whether your target platform supports it before relying on it.

Q. Is anything sent to a server?

A. No. The Markdown is parsed and rendered entirely in your browser.