lazy developers blog

react-markdownをv5からv6へアップデートする

Next.jsでreact-markdwonをv5からv6へアップデートした時の備忘録です。

環境

  • react-markdown : 5.0.3 -> 6.0.2
  • rehype-raw : 5.1.0

アップデートに伴う修正点

修正点は以下のようになりました。

  • sourceからchildrenに変更
  • renderersからcomponentsに変更
    • それに伴いlinkaに変更
    • CodeBlockの修正
  • escapeHtml={false}からrehypePlugins={[rehypeRaw]}に変更

それぞれ個別に見ていきます。

sourceからchildrenに変更

markdownを受け取るpropsがsourceからchildrenに変更になっているため、名前を変更します。

renderersからcomponentsに変更

v5まではrenderersに、markdownに対応した構文(link,delete,break,etc)を指定するようになっており、ユーザーはマークダウンの特性(linklinkReferenceの違いなど)を知らなければいけませんでした。 それがv6ではcomponentsに変更され、受け取る値もhtmlのマークアップに対応した形(a,del,br,etc)に変更されました。

CodeBlockの修正

上記に伴い、CodeBlockを修正する必要がありました。主な変更点は次の通りです。

  • propsの型の変更
    • v5ではrenderersinlineCodecode別々に分かれていたが、v6ではcomponentscodeに統一されたため、inlineのプロパティが追加された
    • languageがなくなり、classMameから取得しなければいけなくなった
    • valueからchildrenに変更
  • 最終行の改行を削除する

CodeBlockの変更前と変更後のソース

  • 変更前(v5)
CodeBlock.tsx
1import { Prism } from 'react-syntax-highlighter';
2import { a11yDark } from 'react-syntax-highlighter/dist/cjs/styles/prism';
3
4type Props = {
5  value: string;
6  language: string;
7};
8
9export const CodeBlock = ({ value, language }: Props) => {
10  const [lang, title] = language ? language.split(':') : ['', null];
11  return (
12    <div className="code-block">
13      {title && <div className="code-block-title">{title}</div>}
14      <div className="code-block-body">
15        <Prism language={lang} style={a11yDark}>
16          {value}
17        </Prism>
18      </div>
19    </div>
20  );
21};
  • 変更後(v6)
CodeBlock.tsx
1import { CodeComponent, ReactMarkdownNames } from 'react-markdown/src/ast-to-react';
2import { Prism } from 'react-syntax-highlighter';
3import { a11yDark } from 'react-syntax-highlighter/dist/cjs/styles/prism';
4
5export const CodeBlock: CodeComponent | ReactMarkdownNames = ({ inline, className, children }) => {
6  const match = /language-(\w+)(:.+)?/.exec(className || '');
7  const [, title] = match && match[2] ? match[2].split(':') : ['', null];
8  const lang = match && match[1] ? match[1] : '';
9  return !inline ? (
10    <div className="code-block">
11      {title && <div className="code-block-title">{title}</div>}
12      <div className="code-block-body">
13        <Prism language={lang} style={a11yDark}>
14          {String(children).replace(/\n$/, '')}
15        </Prism>
16      </div>
17    </div>
18  ) : (
19    <code className={className}>{children}</code>
20  );
21};

escapeHtml={false}からrehypePlugins={[rehypeRaw]}に変更

rehypePluginsが追加されたことで、rehypeのプラグインが使用できるようになりました。そのため、v5に存在したいくつかのHTMLパーサーのオプションは廃止されました。allowDangerousHtml,escapeHtmlの代わりにrehype-rawプラグインを使用しています。

変更前と変更後のソース

変更前と変更後の該当ソースの部分は以下の通りとなります。今回は修正していませんが、pluginsはv7ではdeprecateとなるため、代わりのremarkPluginsに変更しておいてもいいと思います。

  • 変更前(v5)
1<ReactMarkdown
2  source={markdown}
3  renderers={{
4    code: CodeBlock,
5    link: LinkBlock,
6  }}
7  plugins={[gfm]}
8  escapeHtml={false}
9>
  • 変更後(v6)
1<ReactMarkdown
2  children={markdown}
3  components={{
4    code: CodeBlock,
5    a: LinkBlock,
6  }}
7  plugins={[gfm]}
8  rehypePlugins={[rehypeRaw]}
9>

おわりに

分かりづらかったrenderersが変更されたり、rehypeのエコシステムが追加されたりしたことで使いやすくなった印象がありました。