【react-markdown】コードブロックを追加してハイライトする
2022-04-25
2022-04-25
はじめに
昨日たべた柿のドライフルーツ、おいしかった、こふです。
本記事では 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
のコンポーネントを使い、Props
に string
型の 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
型をインポートします。
正規表現の説明と方針に関しては以下で解説しています。
話はそれますが、言語(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> )}
ここで以下のパーツを作っています。ファイルの名前を見せることで伝わりやすくする目的です。

<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
のコードのデザインです。特にこだわりはありません。

他のやり方
数多くのライブラリがあるので、やり方は無限大です。
しかし、ハイライトは Prism.js
というのが一般的に普及しており、使っておけば問題ないといえます。
markdown
をパースするライブラリはラップしている react-markdown
が楽でおすすめです。
が、かゆいところに手が届かないので書き換え予定です。後日、記事にします。
さいごに
最後まで読んでいただきありがとうございました。
これでコードを綺麗に見せることができます、どんどん書いたコードを貼り付けよう!
参考
- https://github.com/react-syntax-highlighter/react-syntax-highlighter
- https://github.com/remarkjs/react-markdown