Generate ToC in NextJS Blog

Avatar

David / 2022-06-10



Generate ToC in NextJS Blog

I combine my blog with my personal website recently, but there isn't a ToC library, so I wrote it myself!

Get h2 text from content

I get my blog content in getStaticProps, and use js array to tidy up the h2 context, method you use will depend on cms you use, personally I use graphcms, and this picture is how they serve the data.

Screen Shot 2022-06-10 at 14.22.46.png

So I can just add a new var called 'anchor' in return props , it is an array of string of h2.

props: {
      anchor: posts[0].content.raw.children
        .filter(children => (children.type === 'heading-two'))
        .map(h2 => (h2.children[0].text))
      // [ 'Planetscale Introduction', 'How To Use', 'Cool Feature' ]
    }

Then we finish the tidy part.

Pass h2 text into ToC component

Below is my ToC component,

const ToC = ({ anchor }) => {
  return (
      <>
        <hr className={'m-4'}/>
        <div className={'px-4'}>
          Table of Contents
          <ul>
            {
              anchor.map(a => (
                  <li key={a}>
                    <a href={`#${a.replace(/ /g, '-').toLowerCase()}`}>{a}</a>
                  </li>
              ))
            }
          </ul>
        </div>
        <hr className={'m-4'}/>
      </>
  )
}

I have also done some text manipulation (change space to -), in case of lots of%20 show in the search bar, which look unprofessional IMO.

It is just simple a tag, and it will go to coresponsding position of id after clicking the text.

Add id to h2

Whether render library you use, it shold have a prop allow you to use your own component to render the content, so I replace default h2 with my custom H2 component, like code below:

const H2 = ({ children }) => {
  return (
      <h2 id={children.props.content[0].text.replace(/ /g, '-').toLowerCase()} className={'hover:after:content-["#"] after:ml-1 after:prose-lg relative'}>
        <a href={`#${children.props.content[0].text.replace(/ /g, '-').toLowerCase()}`} className={'border-none w-full absolute h-full'} />
        {children}
      </h2>
  )
}

That is the id part, after finishing this part, you should be able to jump to the position of that h2 text.

Can you jump smoothly?

Finally, for better user experience, we usually will add some page animation to make the page smoothly, so let add some css in global.css:

html {
  scroll-behavior: smooth;
}

That is!