本サイトはプロモーションが含まれています

Gatsby.jsで作ったブログにタグ機能を追加する

導入

今回は Gatsby.js で作ったブログにタグ機能を追加する手法を紹介します。
記事にタグをつけることで、関連するワードの記事を一目で確認できるため、ぜひ追加したい機能です。
Gatsby Starter Blogをテンプレートとして使い、これにタグ機能を追加します。


ファビコンや記事投稿表示、フォントの変更などの方法は前回の記事で紹介したため、気になる方は確認していただけると嬉しいです。



タグ機能の実装

タグ機能の追加は公式も手順を出しています。(ただし英語です)
https://www.gatsbyjs.com/docs/adding-tags-and-categories-to-blog-posts/
この記事では、日本語ユーザー向けに修正を加え、手順を紹介します。


まず、ブログの投稿一覧ページとブログの各記事にタグを追加します。
Gatsby.js の内部では GraphQL を用いて markdown から必要な情報を取得し、html に変換します。
タグを追加したい記事の markdown の先頭に以下のように tags の情報を追加します。

---
title: Gatsby.jsで作ったブログをカスタマイズする1(ファビコン・記事投稿表示・フォントの変更)
date: "2022-02-06T22:12+09:00"
tags: ["ブログ開発", "Gatsby.js"]
---

この書き方は Frontmatter と呼ばれる markdown にメタ情報を埋め込むための形式で、Jekyll が始まりのようです。
(*参考 https://jekyllrb.com/docs/front-matter/)
次に、gatsby-node.js の type Frontmatter の部分に、以下のように tags の型を設定します。

type Frontmatter {
title: String
description: String
date: Date @dateformat
tags: [String!] // 追加
}

これは null でない String 型の配列(配列自体が null であってもよい)を意味します。
これを追加しないとエラーが出ます。


続いて、src/pages/index.js や src/templates/blog-post.js の pageQuery で tags を追加します。
これで、各ページの tags のデータを取得できるようになります。

export const pageQuery = graphql`
query {
site {
siteMetadata {
title
}
}
allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
nodes {
excerpt
fields {
slug
}
frontmatter {
date
title
description
tags // 追加
}
}
}
}

最後に、graphQL で取得したデータを表示します。

const tags = post.frontmatter.tags
<p>{moment(post.frontmatter.date).format(`YYYY年MM月DD日 HH:mm`)}</p>
<div className="tags-article">
{tags && tags.length > 0 && tags.map(tag => {
return (
<button>{tag}</button>
)
})}
</div>

これを CSS で整えるとこのような感じになります。
タグ表示



タグページの追加

次に、各タグをクリックしたときにそのタグが付いてるページ一覧を表示できるようにします。


まず、gatsby-node.js を編集し、各タグの記事一覧ページを生成します。
URL をケバブケースにするために lodash をインポートします。
ケバブケースとはハイフン(-)で単語を区切ることを言うそうです。

const _ = require("lodash")

そして、tags.js という各タグの記事一覧ページのテンプレートを定義します。

const blogPost = path.resolve(`./src/templates/blog-post.js`)
const tagTemplate = path.resolve("src/templates/tags.js") // 追加

そして、情報を取得する一覧の GraphQL を以下のように変更します。

const result = await graphql(
`
{
postsRemark: allMarkdownRemark(
sort: { fields: [frontmatter___date], order: ASC }
limit: 2000
) {
nodes {
id
fields {
slug
}
frontmatter {
tags
}
}
}
tagsGroup: allMarkdownRemark(limit: 2000) {
group(field: frontmatter___tags) {
fieldValue
}
}
}
`
)

全ページの投稿情報を以下のように変更し、

const posts = result.data.postsRemark.nodes

各タグのページを作成してこのステップは完了です。
これで、/tags/{タグ名}でそのタグが付いた記事の一覧ページが作成されます。

const tags = result.data.tagsGroup.group
if (tags.length > 0) {
tags.forEach(tag => {
createPage({
path: `/tags/${_.kebabCase(tag.fieldValue)}/`,
component: tagTemplate,
context: {
tag: tag.fieldValue,
},
})
})
}

次に、先ほど tagTemplate として読み込んだタグページのテンプレートを追加します。
src/templates/tags.js を作成し、以下のように記述します。

import React from "react"
import { Link, graphql } from "gatsby"
import moment from "moment"
import kebabCase from "lodash/kebabCase"
import Layout from "../components/layout"
import Seo from "../components/seo"
const Tags = ({ data, pageContext, location }) => {
const siteTitle = data.site.siteMetadata?.title || `Title`
const { totalCount } = data.allMarkdownRemark
const posts = data.allMarkdownRemark.nodes
const { tag } = pageContext
if (posts.length === 0) {
return (
<Layout location={location} title={siteTitle}>
<Seo title={`タグ: "${tag}" (0記事)`} />
<p>該当するタグの投稿記事がありません。</p>
</Layout>
)
}
const tagHeader = `タグ: "${tag}" (${totalCount}記事)`
return (
<Layout location={location} title={siteTitle}>
<Seo title={tagHeader} />
<h1>{tagHeader}</h1>
<ol style={{ listStyle: `none` }}>
{posts.map(post => {
const title = post.frontmatter.title || post.fields.slug
const tags = post.frontmatter.tags
return (
<li key={post.fields.slug}>
<article
className="post-list-item"
itemScope
itemType="http://schema.org/Article"
>
<header>
<h2>
<Link to={post.fields.slug} itemProp="url">
<span itemProp="headline">{title}</span>
</Link>
</h2>
<small>
{moment(post.frontmatter.date).format(
`YYYY年MM月DD日 HH:mm`
)}
</small>
<div className="tags-index">
{tags &&
tags.length > 0 &&
tags.map(tag => {
return (
<Link to={`/tags/${kebabCase(tag)}/`} itemProp="url">
<button>{tag}</button>
</Link>
)
})}
</div>
</header>
<section>
<p
dangerouslySetInnerHTML={{
__html: post.frontmatter.description || post.excerpt,
}}
itemProp="description"
/>
</section>
</article>
</li>
)
})}
</ol>
</Layout>
)
}
export default Tags
export const pageQuery = graphql`
query ($tag: String) {
site {
siteMetadata {
title
}
}
allMarkdownRemark(
limit: 2000
sort: { fields: [frontmatter___date], order: DESC }
filter: { frontmatter: { tags: { in: [$tag] } } }
) {
totalCount
nodes {
excerpt
fields {
slug
}
frontmatter {
date
title
description
tags
}
}
}
}
`

このようにして追加したタグページがこちらです。
各タグの記事一覧ページ

タグへのリンクの追加

最後に、各タグをクリックするとそのタグの記事一覧ページに飛ぶようにします。
リンクの追加は簡単で、<button> タグで表現していたタグを以下のように<Link>タグでリンク先の URL とともに囲うだけです。
これを src/pages/index.js と src/templates/tags.js、src/templates/tags.js で行います。

<Link to={`/tags/${kebabCase(tag)}/`} itemProp="url">
<button>{tag}</button>
</Link>

kebabCaseを使うために、あらかじめインポートするのを忘れないようにしてください。

import kebabCase from "lodash/kebabCase"


終わりに

今回はブログには欠かせないタグ機能を追加しました。
次回は、Gatsby.js の SEO 対策を紹介する予定です。