Back to blog
Apr 25, 2022
3 min read

【react-markdown】コードブロックを追加してハイライトする

react-markdownでプログラミング言語をカラフルに魅せませんか?ライブラリをインストールし、テーマを探すだけ。

はじめに

昨日たべた柿のドライフルーツ、おいしかった、こふです。

本記事では react-markdown を使って任意のプログラミング言語を書ける場所と、ハイライト(色付け)を行います。

環境

React を使います。必要な npm ライブラリは適宜インストールしてください。

インストール後の Warning が出る方は以下の記事を参考に修正してください。

更新予定:npm install 時のエラーの解決方法 3 ステップ

terminal

 node -v
v16.14.0

package.json

...
"dependencies": {
    ...
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-markdown": "^8.0.1",
    "react-syntax-highlighter": "^15.5.0",
    ...
  },
  "devDependencies": {
    ...
    "autoprefixer": "^10.4.0",
    "eslint": "^8.13.0",
    "postcss": "^8.4.5",
    "remark-parse": "^10.0.1",
    "tailwindcss": "^3.0.7",
    "typescript": "4.5.4"
    ...
  }
...

ソースコード

<ReactMarkdown
  className="省略"
  children={markdown}
  components={articleComponents}
/>
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { atomDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
import { CodeComponent } from "react-markdown/lib/ast-to-react";

const customCode: CodeComponent = ({ inline, className, children }) => {
  const style = atomDark;
  const match = /language-(\w+)(:?.+)?/.exec(className || "");
  const lang = match && match[1] ? match[1] : "";
  const name = match && match[2] ? match[2].slice(1) : "";
  if (lang === "link") {
    // blog card
  } else if (lang === "twitter") {
    // twitter embed
  }
  return !inline && match ? (
    <>
      {name && (
        <span className="rounded-md bg-stone-200 py-1 px-2 text-sm dark:bg-stone-600">
          {name}
        </span>
      )}
      <SyntaxHighlighter
        children={String(children).replace(/\n$/, "")}
        style={style}
        language={lang}
        PreTag="div"
        className="border-2 text-base dark:border-stone-400 md:text-lg"
      />
    </>
  ) : (
    <code className="mx-1 rounded-md bg-stone-200 py-1 px-2 text-red-600 dark:bg-stone-600 dark:text-red-300">
      {children}
    </code>
  );
};
export const articleComponents = {
  code: customCode,
};

ソースコード解説

分割し、説明を加えます。

ArticleContainer.tsx

<ReactMarkdown
  className=""
  children={markdown}
  components={articleComponents}
/>

これは react-mardown のコンポーネントを使い、Propsstring 型の markdown とカスタムしたコンポーネントを渡します。

ArticleComponents.tsx

import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
import { atomDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'
import { CodeComponent } from 'react-markdown/lib/ast-to-react'

const customCode: CodeComponent = ({ inline, className, children }) => {
  const style = atomDark
  const match = /language-(\w+)(:?.+)?/.exec(className || '')
  const lang = match && match[1] ? match[1] : ''
  const name = match && match[2] ? match[2].slice(1) : ''
  if (lang === 'link') {
    // blog card
  } else if (lang === 'twitter') {
    // twitter embed
  }

ハイライトで囲う Prism、コードのカラーを入れてくれるテーマから atomDark、そして react-markdown から CodeComponent 型をインポートします。

正規表現の説明と方針に関しては以下で解説しています。

https://blog.cohu.dev/react-markdown-blog-card#どうやるか

話はそれますが、言語(lang)ごとに出し分けていて、ブログカード・Twitter 埋め込みをしている形です。

style 変数にテーマ情報を代入します。もしダークモードでは明るい色にしたいのであれば、三項演算子などを使って代入を分けてください。


余談ですが、このサイトでは投稿日時点でハイライトのテーマは一種類に限定しています。

理由は、デフォルトの CSS 値の上書きが面倒だからです。

テーマの種類によって初期の CSS にフォントサイズが与えられているものがあります。

その場合、Tailwind CSS を使っているのですが上書きができません。

インラインの CSS を核などで対処は可能ですし、他の CSS ファイルから読み込むことも、styled-jsx を使うことも可能です。

しかし、あまり明るい色にする価値を感じていないので、一種類で大丈夫という判断です。

今後 react-markdown から軽いライブラリに変更予定なので、仮置きみたいなものです。

...
  return !inline && match ? (
    <>
      {name && (
        <span className="rounded-md bg-stone-200 py-1 px-2 text-sm dark:bg-stone-600">
          {name}
        </span>
      )}

ここで以下のパーツを作っています。ファイルの名前を見せることで伝わりやすくする目的です。

![ファイルのパーツ attachments/2022-04-25-11-22-31.png)

    <SyntaxHighlighter
    children={String(children).replace(/\n$/, '')}
    style={style}
    language={lang}
    PreTag="div"
    className="border-2 text-base dark:border-stone-400 md:text-lg"
    />
</>
) : (

ここがコード本体の場所で、子要素にマークダウンを渡します。

マークダウンはパースすると改行が入るので、replace で取り除いています。

replaceのドキュメント:https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/replace

style にテーマを、language にプログラミング言語を入れます。

    <code className="mx-1 rounded-md bg-stone-200 py-1 px-2 text-red-600 dark:bg-stone-600 dark:text-red-300">
      {children}
    </code>
  )
}
export const articleComponents = {
  code: customCode,
}

以下のような inline のコードのデザインです。特にこだわりはありません。

![inline のコードのデザイン attachments/2022-04-25-11-24-05.png)

他のやり方

数多くのライブラリがあるので、やり方は無限大です。

しかし、ハイライトは Prism.jsというのが一般的に普及しており、使っておけば問題ないといえます。

markdown をパースするライブラリはラップしている react-markdown が楽でおすすめです。

が、かゆいところに手が届かないので書き換え予定です。後日、記事にします。

おわりに

最後まで読んでいただきありがとうございました。

これでコードを綺麗に見せることができます、どんどん書いたコードを貼り付けよう!

参考