はじめに: Markdownをフレームワークの垣根を越えて使う
ブログや技術文書を作成する際、Markdownを様々なフレームワークで利用することになります。問題は、フレームワークごとにMarkdownのレンダリング方法が少しずつ異なり、特にインデント(字下げ)処理で頭を悩ませることが多いのではないでしょうか。
多くのMarkdownパーサーはインデントを無視せず、4スペース以上の空白をコードブロックと見なします。そのため、コードの構造上どうしてもインデントが必要な状況(例: HTML内部にMarkdownを埋め込む場合)では、意図しない<pre><code>タグが生成されてしまう問題が発生します。
この記事では、このようなインデント問題を解決し、どのフレームワークでも同じ方法でMarkdownをレンダリングできるユーティリティを紹介します。自作のユーティリティのため、足りない部分もあるかもしれませんが、実務で十分に検証済みの方法ですので、ぜひ参考にしてください。

本論1: インデント問題の解決とユーティリティの使い方
なぜ従来のMarkdownライブラリは不便なのか?
まず、私が直面した問題を示します。多くのMarkdownライブラリは、以下のようなコードをパースする際にインデントをコードブロックと誤認します。
<div class="container">
<p>これは段落です。</p>
<p>2番目の段落です。</p>
</div>
上記のMarkdownをパースすると、ライブラリは通常次のように処理します。
<div class="container">
<pre><code><p>これは段落です。</p>
<p>2番目の段落です。</p>
</code></pre>
</div>
これでは、意図しないコードブロックが生成され、レイアウトが崩れてしまいます。そのため、やむを得ずインデントを全て削除してMarkdownを書くことになりますが、これはコードの可読性を著しく低下させます。
解決策: カスタムMarkdownユーティリティ
この問題を解決するために、Markdownを処理するユーティリティを作成しました。核心は、インデントされた空白を無視して、正しいHTMLタグを生成することです。
// @splendidlabz/utils パッケージから markdown ユーティリティをインポートします。
import { markdown } from '@splendidlabz/utils';
// 基本的な使い方: 通常のMarkdownをHTMLに変換します。
const html = markdown('# こんにちは!');
// オプションの使い方: inline オプションを true にすると、<p>タグなしでインラインHTMLのみを返します。
const inlineHtml = markdown('**強調されたテキスト**', { inline: true });
console.log(html); // <h1>こんにちは!</h1>
console.log(inlineHtml); // <strong>強調されたテキスト</strong>
このユーティリティは、単にMarkdown構文をサポートするだけでなく、コードのインデント有無に関係なく安定してパースできます。それでは、各フレームワークでの使用方法を見ていきましょう。

本論2: フレームワーク別の適用方法と注意点
1. Astroでの使用
Astroは、.astroファイル内でコンポーネントベースにMarkdownを処理できます。以下は、Markdownコンポーネントを作成する例です。
---
// Markdown.astro
import { markdown } from '@splendidlabz/utils';
// Propsで content またはスロットを受け取ります。
const { inline = false, content } = Astro.props;
const slotContent = await Astro.slots.render('default');
// スロットまたはPropsで渡されたコンテンツをMarkdownに変換します。
const html = markdown(content || slotContent, { inline });
---
<!-- 変換されたHTMLを出力します。 -->
<Fragment set:html={html} />
使用方法は非常に簡単です。コンポーネントをインポートして、コンテンツを渡すだけです。
---
import Markdown from '../components/Markdown.astro';
---
<Markdown>
### このようにインデントされていても
問題なくパースされます。
</Markdown>
2. Svelteでの使用
Svelteは動的スロットコンテンツを読み取れないため、Propsでコンテンツを受け取る方式が適しています。
<!-- Markdown.svelte -->
<script>
import { markdown } from '@splendidlabz/utils';
// Propsを定義します。
export let content = '';
export let inline = false;
// Markdownを変換します。
$: html = markdown(content, { inline });
</script>
<!-- 変換されたHTMLを出力します。 -->
{@html html}
使用例は以下の通りです。
<script>
import Markdown from './Markdown.svelte';
const content = `
## Svelteでも
このように使用できます。
`;
</script>
<Markdown {content} />
3. ReactおよびVueでの使用
ReactとVueも同じ原理で適用できます。ただし、各フレームワークの構文に合わせてコンポーネントを構成する必要があります。この部分は、ぜひご自身で実装してみてください。上記の例を基に十分に拡張できるでしょう。
注意点と制限
このユーティリティは、インデント問題の解決に焦点を当てています。しかし、万能ではありません。複雑なMarkdown(例: テーブル、脚注)を使用する場合は、既存のパーサーと同じ制限を持つ可能性があります。実際のプロダクション環境に導入する前に、十分なテストが必要である点をぜひ覚えておいてください。

まとめ: 実務適用のアドバイスと次のステップ
今回紹介したユーティリティを使用することで、フレームワークに依存せず、Markdownのインデント問題から解放されます。特に、複数のフレームワークを行き来しながら開発されている方にとっては、非常に有用だと考えられます。
実務適用のためのヒント
- 共通ユーティリティとして管理: このユーティリティを各プロジェクトにコピーして使用するのではなく、別のパッケージとしてまとめ、共通で管理することをお勧めします。
- テストの強化: Markdownパースは、想像以上に多くのエッジケースが存在します。様々な状況を想定したテストコードを作成しておくと良いでしょう。
合わせて読みたい記事
この記事でMarkdown処理の悩みが解決できたでしょうか?以下の内容も確認すると、開発効率の向上に役立つでしょう。
- Claude Opus 4.7 Fast Mode公開 2.5倍高速な出力、実務適用ガイド
- Vercel CDNダッシュボードが完全に刷新されました (リアルタイムトラフィック、キャッシュパージ、ルーティングまで)
この記事が役に立ったなら、他の開発者にも共有していただけると幸いです。ご質問はコメント欄にお願いします。
この記事は原文を基に、日本の開発者向けに再構成したものです。