Gatsby.js ブログへの目次追加

どうも、うしじです。

Gatsby.js + Netlifyへブログを移行してから、少しずつカスタマイズを進めています。まずは、ブログに目次を追加したので、その方法について記載したいと思います。



利用したGatsbyプラグイン

本ブログは、Markdownで記載しています。Markdownから自動で目次を作成するために、今回は下記の2つのプラグインを用いました。



Markdownから目次の抽出

目次を自動で抽出するには、gatsby-transformer-remarkを用います。
本ブログでは、既にgatsby-transformer-remarkを利用しているので、そのまま用います。


下記のように、blogTemplate.jsを更新します。これだけで、自動でブログの各ページに目次を挿入することができます。更新のポイントは下記の2点。

  • graphql に tableOfContents の記述を追加
  • ブログコンテンツの前に dangerouslySetInnerHTML={{ __html: tableOfContents }} の記述を追加

import React from "react"
import Helmet from 'react-helmet';
import { graphql } from "gatsby"
import Layout from "../components/layout"

export default function Template({
  data,
}) {
  const { site, markdownRemark } = data
  const { siteMetadata } = site
  const { frontmatter, tableOfContents ,html } = markdownRemark
  return (
    <Layout>
      <Helmet>
        <title>{frontmatter.title} | {siteMetadata.title}</title>
        <meta name="description" content={frontmatter.metaDescription} />
      </Helmet>
      <div className="blog-post-container">
        <article className="post">
          
          {!frontmatter.thumbnail && (
            <div className="post-thumbnail">
              <h1 className="post-title">{frontmatter.title}</h1>
              <div className="post-meta">{frontmatter.date}</div>
            </div>
          )}
          {!!frontmatter.thumbnail && (
            <div className="post-thumbnail" style={{backgroundImage: `url(${frontmatter.thumbnail})`}}>
              <h1 className="post-title">{frontmatter.title}</h1>
              <div className="post-meta">{frontmatter.date}</div>
            </div>
          )}
          <div
            className="table-of-content"
            dangerouslySetInnerHTML={{ __html: tableOfContents }}
          />
          <div
            className="blog-post-content"
            dangerouslySetInnerHTML={{ __html: html }}
          />
        </article>
      </div>
    </Layout>
  )
}

export const pageQuery = graphql`
  query($path: String!) {
    site {
      siteMetadata {
        title
      }
    }
    markdownRemark(frontmatter: { path: { eq: $path } }) {
      html
      tableOfContents(
        absolute: false
        pathToSlugField: "frontmatter.path"
        maxDepth: 3
      )
      frontmatter {
        date(formatString: "MMMM DD, YYYY")
        path
        title
        thumbnail
        metaDescription
      }
    }
  }
`


目次のスタイルシート更新

上記の対応だけだと、htmlのリストそのままで出力されるので、global.scss を更新します。 今回は、下記のようなスタイルシートにしました。

  .table-of-content {
    max-width: 600px;
    margin: 2rem auto;
    border: solid #dadada 1px;
    border-radius: 8px;
    box-shadow :0px 0px 5px silver;
    padding: 0.5rem 0.5rem 0.5rem 2rem;
    line-height: 1.5;
    &:before {
      content: "目次";
      font-size: 110%;
      font-weight: bold;
    }
  }


見出しにID挿入

最後に、Markdownの見出しに、自動でIDを挿入するようにします。これによって、目次の各見出しをクリックした際に、その目次へ移動できるようにします。
ID挿入には、gatsby-remark-autolink-headersを用います。


gatsby-remark-autolink-headersをインストールし、下記のように、gatsby-config.jsに追加するだけで、IDを自動で挿入できます。


plugins: [
{
    resolve: `gatsby-transformer-remark`,
    options: {
    plugins: [
    {
        resolve: `gatsby-remark-autolink-headers`,
        options: {
        offsetY: `30`,
        icon: false,
        className: `remark-autolink-headers`,
        maintainCase: false,
        },
    },
    {
        resolve: `gatsby-remark-prismjs`,
        options: {
        classPrefix: "language-",
        inlineCodeMarker: null,
        aliases: {},
        showLineNumbers: false,
        noInlineHighlight: false,
        },
    }],
    },
},


これで目次の完成です!