unified を使ってオレオレ Markdown を ReactElement に変換する

2 min read読了の目安(約2300字

一連の unified 記事の最後です。好きなように拡張した Markdown を解析して ReactElement に変換し、レンダリングする方法を書きます。

わからない言葉は用語をまとめた記事を参照してみてください。

現在のベストプラクティス

2021 年 4 月現在、次のパッケージを組み合わせて使うのが良さそうです。

コード例

以前作った Zenn のメッセージ記法を解釈するプラグインを使う際のコード例です。

processor.ts
import React from "react";
import unified from "unified";
import parser from "remark-parse";
import mdast2hast from "remark-rehype";
import compiler from "rehype-react";

import { attacher, handler } from "./zenn-message";

const processor = unified()
  .use(parser)
  .use(attacher)
  .use(mdast2hast, { handlers: { message: handler } })
  .use(compiler, { createElement: React.createElement });

export default processor;

変換処理をこのように定義しておいて、次のように使います。

App.tsx
import React from "react";
import processor from "./processor";

const SAMPLE = `
:::message
This is a message
:::

:::message
But this is not a message
::

~~~markdown
# Heading 1

**strong**
~~~
`;

function App() {
  return (
    <>
      <h1>This is a Markdown</h1>
      {processor.processSync(SAMPLE).result}
    </>
  );
}

export default App;

これを表示させると、次のようになります。

表示例

意図通りにレンダリングされていますね。

その他の選択肢

remark の中の人に質問して得た情報から判断しています。

remark-react は使わないの?

メンテナンスされなくなる予定です。また、現時点で TypeScript 対応されていません。

じゃあ react-markdown は?

react-markdown は Markdown から ReactElement へライトに変換したい場合を想定したパッケージとのことです。

実際に react-markdown は設定を細かく指定することができない分、次のように使うことができて非常に楽です。

import React from "react";
import ReactMarkdown from "react-markdown";
import { render } from "react-dom";

render(<ReactMarkdown># Hello, *world*!</ReactMarkdown>, document.body);

react-remark では Hooks が用意されてるけど…?

React 17 で動かないようです。深追いする気力がなく…。

ただし、今後これがメインでメンテナンスされていくようです。