ReactMarkDownでエージェントのMD形式の回答をHtmlで表示する


React で Markdown を表示する際に便利な React-Markdown の使い方と、カスタムコンポーネントを適用する方法について解説します。

https://github.com/remarkjs/react-markdown


---

## React-Markdown とは？

**React-Markdown** は、Markdown 形式のテキストを React コンポーネント内で簡単にレンダリングできるライブラリです。  
通常の HTML に変換するだけでなく、カスタムコンポーネントを適用することで、特定の要素の見た目や動作を変更できます。

## なんでこのライブラリを使ったの？

アサインしているプロジェクトではAIエージェントを使用したチャットボットを作成しています。
その返答を表示するための技術選定という前提で理由を上げていきます！

### 1. AI は構造化された文章を出力しやすい  
Markdown（MD）形式は、見出し (`#`)、リスト (`-`)、コードブロック（```） などの記法が統一されているため、  
AI が **一貫したフォーマットで文章を生成しやすい** という特徴があります。  

特に ChatBot のような応答型のシステムでは、**可読性が高く、フォーマットが明確な出力** が求められるため、  
Markdown を採用することで、回答の一貫性を維持しやすくなります。

---

### 2. AIエージェントとの相性  
BedRock（AWS の大規模言語モデル）などのAIエージェントを利用したChatBotにMarkdownを採用することで、以下のメリットがあります。

- **整った文章で返しやすい** → AIエージェントがフォーマットを維持しやすい  
- **フロントエンドでのレンダリングが容易** → `ReactMarkdown` などのライブラリを使えば簡単に表示できる  
- **箇条書きやコードブロックで情報を整理しやすい** → 手順説明や技術的な回答に適している  

ChatBotの回答をそのままウェブやアプリで表示する場合、Markdownのまま処理すれば 
`ReactMarkdown`などのライブラリを活用してシンプルにレンダリングできるため、実装面でも合理的な選択肢となります。

---

## 使ってみた

## 準備
```terminal
bun intall react-markdown
```

公式はnpm推してましたが、bunでもいけました⭕️

## React-Markdown で Markdown を表示する

ReactMarkdownコンポーネントでは、通常の HTML のように `<h1>` や `<p>` を書くのではなく、Markdown 形式のテキストを `ReactMarkdown` コンポーネントで直接レンダリングできます。

基本的な使い方は以下のとおりです。

```tsx
import ReactMarkdown from 'react-markdown';

const text = `
# これは H1 タイトル
## これは H2 タイトル
- リスト項目 1
- リスト項目 2
`;

export default function App() {
  return <ReactMarkdown>{text}</ReactMarkdown>;
}
```

`#` は `<h1>` に、`##` は `<h2>` に変換され、リスト (`-`) も `<ul><li>` に変換されます。  
`ReactMarkdown` を使うだけで、Markdown を HTML に変換できます。

---

## React-Markdown のカスタムコンポーネント

デフォルトの HTML ではなく、見た目や動作をカスタマイズしたい場合は、`components` を使って特定の要素をカスタムコンポーネントに置き換えることができます。

例えば、`h1` のスタイルを変更する場合、以下のように `h1` をカスタムコンポーネントに置き換えます。

### カスタム h1 コンポーネントの作成

```tsx
'use client';
import type { ClassAttributes, HTMLAttributes } from 'react';
import type { ExtraProps } from 'react-markdown';

export const ReactMarkDownCustomH1 = ({
  children,
}: ClassAttributes<HTMLHeadingElement> & HTMLAttributes<HTMLHeadingElement> & ExtraProps) => {
  return <h1 style={{ color: 'blue', fontSize: '1.5em' }}>{children}</h1>;
};
```

`children` を受け取るだけで、Markdown の `# タイトル` をカスタムできる。  
また、他の HTML 属性（`id`, `className` など）も受け取れるように `props` を適用している。

---

### カスタムコンポーネントの適用

カスタム `h1` コンポーネントを `ReactMarkdown` に適用するには、`components` プロパティを使います。  
以下のように `h1` を `ReactMarkDownCustomH1` に置き換えます。

```tsx
import ReactMarkdown from 'react-markdown';
import { ReactMarkDownCustomH1 } from './ReactMarkDownCustomH1';

const text = `
# これはカスタム H1 タイトル
## これは通常の H2 タイトル
`;

export default function App() {
  return <ReactMarkdown components={{ h1: ReactMarkDownCustomH1 }}>{text}</ReactMarkdown>;
}
```

Markdown の `# H1` を `ReactMarkDownCustomH1` に置き換え、青色 & 1.5em のサイズにカスタマイズ。  
`## H2` はそのまま変換される。

---

## 画像 (`img`) のカスタムコンポーネント

画像 (`img`) もカスタマイズできます。  
`ReactMarkdown` では `src` や `alt` などの `props` が自動で渡されるため、以下のように `max-width` を調整した `img` を作成できます。

```tsx
const ReactMarkDownCustomImage = ({ src, alt }: { src?: string; alt?: string }) => {
  return <img src={src} alt={alt || '画像'} style={{ maxWidth: '100%', borderRadius: '8px' }} />;
};
```

カスタム画像コンポーネントを適用するには、以下のように `components` に `img` を追加します。

```tsx
<ReactMarkdown components={{ h1: ReactMarkDownCustomH1, img: ReactMarkDownCustomImage }}>
  {text}
</ReactMarkdown>
```

画像の `src` と `alt` は `ReactMarkdown` が自動で `props` に渡してくれるので、特別な処理は不要。  
また、`max-width: 100%` を適用することで、画像の横幅を親要素に合わせることができる。

---

## Markdown のテキストは `children` で渡せばよい

React-Markdown のカスタムコンポーネントを作成する際、基本的に Markdown のテキストは `children` として受け取れば問題ありません。

```tsx
const ReactMarkDownCustomH1 = ({
  children,
}: ClassAttributes<HTMLHeadingElement> & HTMLAttributes<HTMLHeadingElement> & ExtraProps) => {
  return <h1 style={{ color: 'blue', fontSize: '1.5em' }}>{children}</h1>;
};
```

`children` を受け取るだけで、Markdown の内容がそのまま渡される。  
不要な `props` を扱わなくても、テキスト情報は `children` で取得可能。

例えば、次のように `ReactMarkdown` に適用すれば、`h1` と `img` の両方をカスタマイズできます。

```tsx
<ReactMarkdown components={{ h1: ReactMarkDownCustomH1, img: ReactMarkDownCustomImage }}>
  {text}
</ReactMarkdown>
```

---

## まとめ

React-Markdown を使うと、Markdown をそのまま HTML に変換できるだけでなく、  
`components` を利用して特定の要素の見た目や動作をカスタマイズできます。

### 基本的な使い方
```tsx
<ReactMarkdown>{text}</ReactMarkdown>
```
このコードだけで Markdown を HTML に変換できる。

### カスタムコンポーネントの適用
```tsx
<ReactMarkdown components={{ h1: ReactMarkDownCustomH1, img: ReactMarkDownCustomImage }}>
  {text}
</ReactMarkdown>
```
カスタム `h1` や `img` を適用し、デザインを調整できる。

React-Markdown を活用することで、Markdown のシンプルさを活かしつつ、柔軟にデザインを調整できるようになりました☺️