💊
2020-12-29

SSGを適用したNextjsで目次を生成する

Next.js

Javascript

SSGを適用したNext.jsで,目次(Table of Content)を生成する方法を紹介します.
h2タグを大目次,h3タグを小目次とし,こちらのような目次を生成することができます.


こちらのブログはNext.jsで作成しており,目次を生成しています.具体的なコードを見たい方は,こちらのリポジトリを参考にしてください.
https://github.com/kawa1214/micro-cms-nextjs-blog-boiler-template

NextjsでSSGを適用する

Next.jsではgetStaticPropsを用いることで,静的なファイルを事前に生成することができます.
./pages/blogs/[id].tsx

export const getStaticProps: GetStaticProps = async context => {  
  const id = context.params.id;
  const key = {
    headers: { 'X-API-KEY': process.env.API_KEY },
  }

  const res = await fetch(process.env.ENDPOINT + '/blogs/' + id, key)
  const blog: BlogType  = await res.json()

  return {
    props: {
      blog: blog,
    },
  }
}

目次を生成する

cheerioでhtmlを解析し,目次を生成します.
今回は,h2を大目次,h3を小目次としています.
./pages/blogs/[id].tsx

const Blog: React.FC<BlogProps> = ({ blog, toc }) => {
  return (
    <>
      <div
          id="blog_toc"
          dangerouslySetInnerHTML={{
            __html: toc,
          }}
      />
    </>
  )
}
export default Blog

const generateTableOfContent = (body: string) => {
  const $ = cheerio.load(body,{ decodeEntities: false })
  let generateHtml = ''
  generateHtml = generateHtml + '<ul>'
  $('h2, h3').each((index, elm) => {
    const text = $(elm).html()
    const tag = $(elm)[0].name


    const refId = $(elm)[0].attribs.id
    generateHtml = generateHtml + 
    `<li class="toc_${tag}" key=${index}>`+
    `  <a href="#${refId}">${text}</a>`+
    '</li>'
  })
  generateHtml = generateHtml + '</ul>'
  return generateHtml
}

export const getStaticProps: GetStaticProps = async context => {  
  const id = context.params.id;
  const key = {
    headers: { 'X-API-KEY': process.env.API_KEY },
  }

  const res = await fetch(process.env.ENDPOINT + '/blogs/' + id, key)
  const blog: BlogType  = await res.json()
  const toc: string = generateTableOfContent(blog.body)

  return {
    props: {
      blog: blog,
      toc: toc,
    },
  }
}


生成したHTMLにCSSを適用します.こちらのサンプルコードはtailwind cssを使用しています.
./styles/Blog.module.css

/* blog table of content*/
#blog_toc ul {
  @apply bg-white rounded px-2 py-2;
}

#blog_toc li {
  @apply ml-4 my-1.5;
}

#blog_toc a:link {
  @apply text-gray-900;
}

#blog_toc a:visited {
  @apply text-gray-900;
}

#blog_toc a:hover {
  color: #4C74B9;
}

#blog_toc a:active {
  color: #4C74B9;
}

#blog_toc .toc_h3 {
  @apply pl-4
}