Gatsbyの記事に目次を作成してみる

目次
目次を作るには?
目次を作るには、gatsby-remark-table-of-contents と gatsby-remark-autolink-headers のプラグインを使えば、サクッと出来ちゃいそうですが…
Graphql のクエリの中に tableOfContents という項目があるので、それと gatsby-remark-autolink-headers で目次を作成してみたいと思います。
gatsby-remark-autolink-headers の使い方は、いろんな記事になっているので、割愛。
以前のブログでも
gatsby-starter-blog を使っていた時は以下の様な手順でやっていました。
コードは以下に掲載されているものでやってみます。
export const pageQuery = graphql`
query ($id: String!) {
markdownRemark(id: { eq: $id }) {
html
tableOfContents
frontmatter {
date(formatString: "MMMM DD, YYYY")
slug
title
}
}
}
`;
http://localhost:8000/\_\_\_graphql で実行すると…
{
"data": {
"markdownRemark": {
"tableOfContents": "<ul>
<li><a href="#%E4%BB%A5%E5%89%8D%E3%81%AE%E3%83%96%E3%83%AD%E3%82%B0%E3%81%AF">以前のブログは</a></li>
<li><a href="#3-%E3%81%8B%E3%82%89-4-%E3%81%AB%E3%82%A2%E3%83%83%E3%83%97%E3%83%87%E3%83%BC%E3%83%88">3 から 4 にアップデート</a></li>
<li><a href="#allmdx-%E3%82%92%E4%BD%BF%E3%81%86">allMdx を使う</a></li>
<li><a href="#staticimage-%E3%81%8C%E4%BE%BF%E5%88%A9%E3%81%9D%E3%81%86">StaticImage が便利そう</a></li>
<li><a href="#css-%E3%81%AF-styled-components">CSS は styled components</a></li>
<li><a href="#%E4%BB%8A%E5%BE%8C%E3%81%AF">今後は</a></li>
</ul>"
}
},
"extensions": {}
}
この様な感じで tableOfContents の値を取得出来ます。
import React from "react";
import { graphql } from "gatsby";
export default function Template({
data, // this prop will be injected by the GraphQL query below.
}) {
const { markdownRemark } = data; // data.markdownRemark holds your post data
const { frontmatter, html, tableOfContents } = markdownRemark;
return (
<div className="blog-post-container">
<div className="blog-post">
<h1>{frontmatter.title}</h1>
<div
className="XXXXXX"
dangerouslySetInnerHTML={{ __html: tableOfContents }}
/>
<h2>{frontmatter.date}</h2>
<div
className="blog-post-content"
dangerouslySetInnerHTML={{ __html: html }}
/>
</div>
</div>
);
}
後はコード内にある html を表示する手順に倣って、tableOfContents を表示させます。
allMdx で同じことを試すと
チュートリアルページに掲載されている、src/pages/blog/{mdx.slug}.js で同じことをすると…
export const query = graphql`
query ($id: String) {
mdx(id: { eq: $id }) {
frontmatter {
title
date(formatString: "MMMM D, YYYY")
}
body
tableOfContents
}
}
`;
tableOfContents をクエリに追加して、
import * as React from "react";
import { graphql } from "gatsby";
import { MDXRenderer } from "gatsby-plugin-mdx";
import Layout from "../../components/layout";
const BlogPost = ({ data }) => {
return (
<Layout pageTitle={data.mdx.frontmatter.title}>
<div
className="XXXXXX"
dangerouslySetInnerHTML={{ __html: data.mdx.tableOfContents }}
/>
<p>{data.mdx.frontmatter.date}</p>
<MDXRenderer>{data.mdx.body}</MDXRenderer>
</Layout>
);
};
markdownRemark の時と同じ様にして、実行してみると…
[object Object]と表示されてしまいます。
http://localhost:8000/\_\_\_graphql を開いて、tableOfContents の中を確かめてみると…
{
"data": {
"mdx": {
"tableOfContents": {
"items": [
{
"url": "#以前のブログは",
"title": "以前のブログは"
},
{
"url": "#3-から-4-にアップデート",
"title": "3 から 4 にアップデート"
},
{
"url": "#allmdx-を使う",
"title": "allMdx を使う"
},
{
"url": "#staticimage-が便利そう",
"title": "StaticImage が便利そう"
},
{
"url": "#css-は-styled-components",
"title": "CSS は styled components"
},
{
"url": "#今後は",
"title": "今後は"
}
]
}
}
},
"extensions": {}
}
そりゃ、表示されないわ…ということで、コンポーネント化してみます。
src/pages/blog/{mdx.slug}.js の記述を変更
import * as React from "react";
import { graphql } from "gatsby";
import { MDXRenderer } from "gatsby-plugin-mdx";
import Layout from "../../components/layout";
import Toc from "../components/toc";
const BlogPost = ({ data }) => {
return (
<Layout pageTitle={data.mdx.frontmatter.title}>
<Toc tableOfContents={data.mdx.tableOfContents.items} />
<p>{data.mdx.frontmatter.date}</p>
<MDXRenderer>{data.mdx.body}</MDXRenderer>
</Layout>
);
};
ざっくりとですが、目次用のコンポーネントを用意してあげると
import React from "react";
const TOC = (props) => {
const data = props.tableOfContents;
return (
<div>
<h4>目次</h4>
<ul>
{data.map((item) => (
<li>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
</div>
);
};
export default TOC;
こんな感じで目次用のコンポーネントを作成してあげたら良さそうです。
参考にさせて頂いた記事
(自分は JS ですが)TypeScript ではこう書けばいいのか…と勉強させていただきました。