😽

Next.jsで究極のマークダウンエディタを作成する話 その2

2025/01/14に公開

ReactMarkdownを使うのが良さそう

前回までの調査でmarkdownをリアルタイムプレビューには成功しました

今回は拡張で欲しい機能を追加できるものの調査と設計をしていきます

1 スタイリングを当てられる

2 コードブロックに言語に応じたハイライトがある

3 htmlを埋め込める

4 github準拠のものが使える

5 Texを用いて数式を扱える

1.スタイリングを当てる

https://github.com/sindresorhus/github-markdown-css/tree/main

こちらのものを使わせていただきました

npm install github-markdown-css

をして、

import "github-markdown-css";

を追記

        <ReactMarkdown className="markdown-body">
          {markdown}
        </ReactMarkdown>

と変更するだけ

image.png

となります!👏

2.コードブロックに言語に応じたハイライトがある

どうやらHighlightのライブラリがたくさんあるらしい
どれを使ってもうまくいかないので現在調査中です

3.htmlを埋め込める

npm i rehype-raw rehype-sanitize
import rehypeRaw from "rehype-raw";
import rehypeSanitize from "rehype-sanitize";
...
    <ReactMarkdown
      rehypePlugins={[rehypeRaw, rehypeSanitize]}
    >
      {markdown}
    </ReactMarkdown>

4.github準拠のものが使える

 npm i remark-gfm
import remarkGfm from "remark-gfm";
...
    <ReactMarkdown remarkPlugins={[remarkGfm]}>
     {markdown}
   </ReactMarkdown>

5.Texを用いて数式を扱える

remark-math rehype-katex
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
...
    <ReactMarkdown remarkPlugins={[remarkMath]} rehypePlugins={[rehypeKatex]}>
      {markdown}
    </ReactMarkdown>
import "katex/dist/katex.min.css";

全てを詰め合わせたサンプル

import { useState, useRef, useEffect } from "react";
import remarkGfm from "remark-gfm";
import ReactMarkdown from "react-markdown";
import remarkMath from "remark-math";
import rehypeKatex from "rehype-katex";
import rehypeRaw from "rehype-raw";
import rehypeSanitize from "rehype-sanitize";

export function MarkdownEditor() {
  const [markdown, setMarkdown] = useState("");

  return (
    <div className="flex gap-5 p-5">
      <div className="flex-1">
        <textarea
          className="w-full p-3 text-lg border border-gray-300 rounded resize-none"
          placeholder="Write your Markdown here..."
          value={markdown}
          onChange={(e) => setMarkdown(e.target.value)}
        />
      </div>
      <div className="flex-1 p-3 border rounded overflow-y-auto max-h-[400px]">
        <ReactMarkdown
          remarkPlugins={[remarkMath, remarkGfm]}
          rehypePlugins={[rehypeRaw, rehypeSanitize,rehypeKatex]}
          className="markdown-body"
        >
          {markdown}
        </ReactMarkdown>
      </div>
    </div>
  );
}

:::note warn
rehypePlugins={[rehypeRaw, rehypeSanitize,rehypeKatex]}
はこの順番でないと正常動作しません
:::

GitHubで編集を提案

Discussion