※記事のURLの一部は広告を含みます。

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

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

2022-04-26

2022-04-26

はじめに

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

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

これのことです。

目次の例

環境

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

terminal

1❯ node -v 2v16.14.0

package.json

1... 2"dependencies": { 3 ... 4 "react": "^17.0.2", 5 "react-dom": "^17.0.2", 6 "react-markdown": "^8.0.1", 7 "react-syntax-highlighter": "^15.5.0", 8 ... 9 }, 10 "devDependencies": { 11 ... 12 "autoprefixer": "^10.4.0", 13 "eslint": "^8.13.0", 14 "postcss": "^8.4.5", 15 "tailwindcss": "^3.0.7", 16 "typescript": "4.5.4" 17 ... 18 } 19...

方針

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

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

ソースコード

  • TOC.tsx
  • H2.tsx

の 2 つを作ります。

TOC.tsx

目次の本体です。

src/components/function/TOC/TOC.tsx
1import React, { FC } from 'react' 2import { HiLink } from 'react-icons/hi' 3import ReactMarkdown from 'react-markdown' 4type Props = { 5 markdown: string 6} 7 8const customH2 = ({ ...props }) => { 9 return ( 10 <li> 11 <a 12 href={`#${props.children}`} 13 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" 14 > 15 <HiLink className="mr-2" /> 16 {props.children} 17 </a> 18 </li> 19 ) 20} 21 22const components = { h2: customH2 } 23 24const TOC: FC<Props> = ({ markdown }) => { 25 return ( 26 <details 27 open 28 className="my-4 rounded-md bg-white p-2 shadow-md hover:cursor-pointer focus:outline-none dark:bg-stone-700 md:p-6" 29 > 30 <summary className="text-base font-semibold text-stone-800 focus:outline-none dark:text-stone-100 md:text-lg"> 31 目次(タップして移動) 32 </summary> 33 <ol className="p-2 md:p-4"> 34 <ReactMarkdown 35 className="md:prose-md dark:prose-invert" 36 children={markdown} 37 allowedElements={['h2']} 38 components={components} 39 /> 40 </ol> 41 </details> 42 ) 43} 44 45 46export default TOC

H2.tsx

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

src/components/Heading/H2/H2.tsx
1import React, { FC } from 'react' 2import { HiLink } from 'react-icons/hi' 3type Props = { 4 label: string 5 classNameText?: string 6} 7const H2: FC<Props> = ({ label, classNameText }) => { 8 return ( 9 <a href={`#${classNameText}`} className="duration-300 hover:opacity-75"> 10 <h2 11 id={classNameText} 12 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" 13 > 14 <HiLink className="mr-2" /> 15 {label} 16 </h2> 17 </a> 18 ) 19} 20 21export default H2

ソースコード解説

TOC.tsx の解説

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

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

src/components/function/TOC/TOC.tsx
1const customH2 = ({ ...props }) => { 2 return ( 3 <li> 4 <a 5 href={`#${props.children}`} 6 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" 7 > 8 <HiLink className="mr-2" /> 9 {props.children} 10 </a> 11 </li> 12 ) 13} 14const components = { h2: customH2 }

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

src/components/function/TOC/TOC.tsx
1const TOC: FC<Props> = ({ markdown }) => { 2 return ( 3 <details 4 open 5 className="my-4 rounded-md bg-white p-2 shadow-md hover:cursor-pointer focus:outline-none dark:bg-stone-700 md:p-6" 6 > 7 <summary className="text-base font-semibold text-stone-800 focus:outline-none dark:text-stone-100 md:text-lg"> 8 目次(タップして移動) 9 </summary> 10 <ol className="p-2 md:p-4"> 11 <ReactMarkdown 12 className="md:prose-md dark:prose-invert" 13 children={markdown} 14 allowedElements={['h2']} 15 components={components} 16 /> 17 </ol> 18 </details> 19 ) 20} 21 22export 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
1import React, { FC } from 'react' 2import { HiLink } from 'react-icons/hi' 3type Props = { 4 label: string 5 classNameText?: string 6} 7const H2: FC<Props> = ({ label, classNameText }) => { 8 return ( 9 <a href={`#${classNameText}`} className="duration-300 hover:opacity-75"> 10 <h2 11 id={classNameText} 12 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" 13 > 14 <HiLink className="mr-2" /> 15 {label} 16 </h2> 17 </a> 18 ) 19} 20 21export default H2

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

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

おわりに

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

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

https://github.com/tscanlin/tocbot

参考




共有する