【react-markdown】目次機能をつくる【tailwind cssのデザイン例あり】の絵文字

【react-markdown】目次機能をつくる【tailwind cssのデザイン例あり】

2022-04-26

2022-04-26

はじめに

キャラメルって美味しい、こふです。

ページの上部にある目次の作り方と、デザイン例を公開します。

これのことです。

環境

React をメインに、CSSTailwind CSS をメインに使います。

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", "tailwindcss": "^3.0.7", "typescript": "4.5.4" ... } ...

方針

markdown の本文のうち、 h2,h3 タグを目次に含めたい場合が多いかと思います。本ブログでは、h2 を見るだけで何をしているか分かることが多いため、h2 のみ採用しています。

つまり、markdownparse するときに h2 のみを受け取り、そこから値を作れば良いだけです。

ソースコード

  • TOC.tsx
  • H2.tsx

の 2 つを作ります。

TOC.tsx

目次の本体です。

src/components/function/TOC/TOC.tsx
import React, { FC } from 'react' import { HiLink } from 'react-icons/hi' import ReactMarkdown from 'react-markdown' type Props = { markdown: string } const customH2 = ({ ...props }) => { return ( <li> <a href={`#${props.children}`} className="inline-flex items-center py-1 text-base text-stone-700 duration-300 hover:text-stone-500 dark:text-stone-100 dark:hover:text-stone-300 md:text-lg" > <HiLink className="mr-2" /> {props.children} </a> </li> ) } const components = { h2: customH2 } const TOC: FC<Props> = ({ markdown }) => { return ( <details open className="my-4 rounded-md bg-white p-2 shadow-md hover:cursor-pointer focus:outline-none dark:bg-stone-700 md:p-6" > <summary className="text-base font-semibold text-stone-800 focus:outline-none dark:text-stone-100 md:text-lg"> 目次(タップして移動) </summary> <ol className="p-2 md:p-4"> <ReactMarkdown className="md:prose-md dark:prose-invert" children={markdown} allowedElements={['h2']} components={components} /> </ol> </details> ) } export default TOC

H2.tsx

これは見出しに用います。

src/components/Heading/H2/H2.tsx
import React, { FC } from 'react' import { HiLink } from 'react-icons/hi' type Props = { label: string classNameText?: string } const H2: FC<Props> = ({ label, classNameText }) => { return ( <a href={`#${classNameText}`} className="duration-300 hover:opacity-75"> <h2 id={classNameText} className="inline-flex items-center text-xl font-semibold text-stone-800 underline underline-offset-4 dark:text-stone-100 md:text-2xl lg:text-3xl" > <HiLink className="mr-2" /> {label} </h2> </a> ) } export default H2

ソースコード解説

TOC.tsx の解説

src/components/function/TOC/TOC.tsx
import React, { FC } from 'react' import { HiLink } from 'react-icons/hi' import ReactMarkdown from 'react-markdown' type Props = { markdown: string }

ライブラリのインポートと TOC コンポーネントに渡す Props の型を定義します。

src/components/function/TOC/TOC.tsx
const customH2 = ({ ...props }) => { return ( <li> <a href={`#${props.children}`} className="inline-flex items-center py-1 text-base text-stone-700 duration-300 hover:text-stone-500 dark:text-stone-100 dark:hover:text-stone-300 md:text-lg" > <HiLink className="mr-2" /> {props.children} </a> </li> ) } const components = { h2: customH2 }

目次中のタップできる見出しです。href を与えて、タップ時に遷移するようにします。遷移先は後の H2 で作ります。

src/components/function/TOC/TOC.tsx
const TOC: FC<Props> = ({ markdown }) => { return ( <details open className="my-4 rounded-md bg-white p-2 shadow-md hover:cursor-pointer focus:outline-none dark:bg-stone-700 md:p-6" > <summary className="text-base font-semibold text-stone-800 focus:outline-none dark:text-stone-100 md:text-lg"> 目次(タップして移動) </summary> <ol className="p-2 md:p-4"> <ReactMarkdown className="md:prose-md dark:prose-invert" children={markdown} allowedElements={['h2']} components={components} /> </ol> </details> ) } export default TOC

html 標準である、折りたたみができる details,summary を使いました。

参考:https://developer.mozilla.org/ja/docs/Web/HTML/Element/details

目次を作るポイントとして、ReactMarkdown に渡すことのできるコンポーネントの指定を h2 のみにします。

h3 も目次に含めたい場合はallowedElements={['h2','h3']}とすれば OK です。

参考:https://github.com/remarkjs/react-markdown#props

それに合わせてconst components = { h2: customH2, h3: customH3 }とし、デザインした h3 を作れば OK です。

H2.tsx の解説

src/components/Heading/H2/H2.tsx
import React, { FC } from 'react' import { HiLink } from 'react-icons/hi' type Props = { label: string classNameText?: string } const H2: FC<Props> = ({ label, classNameText }) => { return ( <a href={`#${classNameText}`} className="duration-300 hover:opacity-75"> <h2 id={classNameText} className="inline-flex items-center text-xl font-semibold text-stone-800 underline underline-offset-4 dark:text-stone-100 md:text-2xl lg:text-3xl" > <HiLink className="mr-2" /> {label} </h2> </a> ) } export default H2

href に渡すことで、タップしたリンクの共有を行うと、適切な位置にスクロールして共有できます。

id に指定することで、https://example.com/path#hoge というときに hoge という id に遷移できます。

さいごに

これで react-markdown に目次を実装することが可能です。

ちなみに、他のライブラリとしてtocbotを使うとリッチな目次も可能です。

参考



アバター

こふ

情報通信を専攻している大学生です。大学(研究)・趣味・アルバイトでプログラムを書いています。ITツール・サービス・文章を創作することが好きです。

ぼくについて

共有する