Gatsby.js スタティックサイトのチュートリアル Part 10
Gatsby.jsでMDXを使用したブログサイトを作成するチュートリアルのPart10です。今回はブログにタグ機能を追加します。下記のような流れで作成します。
- MDXファイルにタグを追加する
- ブログポストのすべてのタグを取得するクエリを作成する
- タグページのテンプレートを作成する
- テンプレートを使用するためにgatsby-node.jsを修正する
- タグリストのページを作成する
- ブログリストにタグを表示する
MDXファイルにタグを追加する
各ブログポストのMDXファイルにタグを追加します。各ファイルのfrontmatterを下記のように変更します。
blog/first-post/index.mdx frontmatter
---
title: "First Post"
date: "2024-04-01"
slug: "first-post"
hero_image: "./sample-image-1.png"
hero_image_alt: "Alt text for your image"
tags: ["Webdev", "HTML"]
---
blog/second-post/index.mdx frontmatter
---
title: "Second Post"
date: "2024-04-02"
slug: "second-post"
hero_image: "./sample-image-2.png"
hero_image_alt: "Alt text for your image"
tags: ["Webdev", "CSS"]
---
blog/third-post/index.mdx frontmatter
---
title: "Third Post"
date: "2024-04-03"
slug: "third-post"
hero_image: "./sample-image-3.png"
hero_image_alt: "Alt text for your image"
tags: ["Webdev", "Javascript"]
---
blog/fourth-post/index.mdx frontmatter
---
title: "Fourth Post"
date: "2024-04-04"
slug: "fourth-post"
hero_image: "./sample-image-4.png"
hero_image_alt: "Alt text for your image"
tags: ["Webdev", "React"]
---
blog/fifth-post/index.mdx frontmatter
---
title: "Fifth Post"
date: "2024-04-05"
slug: "fifth-post"
hero_image: "./sample-image-5.png"
hero_image_alt: "Alt text for your image"
tags: ["Webdev", "Gatsby"]
---
blog/sixth-post/index.mdx frontmatter
---
title: "Sixth Post"
date: "2024-04-06"
slug: "sixth-post"
hero_image: "./sample-image-6.png"
hero_image_alt: "Alt text for your image"
tags: ["Webdev", "Git"]
---
ブログポストのすべてのタグを取得するクエリを作成する
GraphiQLで下記のようにクエリを作成します。
fieldValueでタグ名が取得できているのが確認できます。クエリは下記のようになります。
query {
allMdx(limit: 1000) {
group(field: {frontmatter: {tags: SELECT}}) {
fieldValue
}
}
}
タグページのテンプレートを作成する
それぞれのタグのページを表示できるように下記のようにテンプレートファイルを作成します。
src/templates/tags.js
import * as React from 'react'
import { Link, graphql } from 'gatsby'
import Layout from '../components/layout'
import Seo from '../components/seo'
const Tags = ({ pageContext, data }) => {
const { tag } = pageContext
const { edges, totalCount } = data.allMdx
const tagHeader = `${totalCount} post${
totalCount === 1 ? "" : "s"
} tagged with "${tag}"`
return (
<Layout>
<h1>{tagHeader}</h1>
<ul>
{edges.map(({ node }) => {
const { slug } = node.frontmatter
const { title } = node.frontmatter
return (
<li key={slug}>
<Link to={`/blog/${slug}`}>
{title}
</Link>
</li>
)
})}
</ul>
<Link to="/tags">All tags</Link>
</Layout>
)
}
export const pageQuery = graphql`
query ($tag: String) {
allMdx(
limit: 1000
sort: {frontmatter: {date: DESC}}
filter: {frontmatter: {tags: {in: [$tag]}}}
) {
edges {
node {
frontmatter {
title
date(formatString: "MMMM D, YYYY")
tags
slug
}
id
excerpt
}
}
totalCount
}
}
`
export const Head = () => <Seo />
export default Tags
テンプレートを使用するためにgatsby-node.jsを修正する
gatsby-node.jsを下記のように変更します。タグ名をケバブケースに統一するためlodashライブラリを使用します。またブログポスト一覧とタグ一覧を取得するために、どちらもallMdxを使用するため、それぞれindexRemarkとtagsGroupと名前を付けて使い分けます。
const path = require(`path`)
const { paginate } = require(`gatsby-awesome-pagination`)
const _ = require(`lodash`)
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions
const result = await graphql(`
{
indexRemark: allMdx(
sort: {frontmatter: {date: DESC}},
limit: 1000
) {
edges {
node {
frontmatter {
slug
tags
}
}
}
}
tagsGroup: allMdx(limit: 1000) {
group(field: {frontmatter: {tags: SELECT}}) {
fieldValue
}
}
}
`)
if (result.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`)
return
}
const indexTemplate = path.resolve("src/templates/blog.js")
const index = result.data.indexRemark.edges
paginate({
createPage,
items: index,
itemsPerPage: 2,
pathPrefix: "/blog",
component: indexTemplate,
})
const tagTemplate = path.resolve("src/templates/tags.js")
const tags = result.data.tagsGroup.group
if (tags.length > 0) {
tags.forEach(tag => {
createPage({
path: `/tag/${_.kebabCase(tag.fieldValue)}/`,
component: tagTemplate,
context: {
tag: tag.fieldValue,
},
})
})
}
}
タグリストのページを作成する
タグの一覧を表示するページを作成します。
src/pages/tags.js
import * as React from 'react'
import { Link, graphql } from 'gatsby'
import kebabCase from 'lodash/kebabCase'
import Layout from '../components/layout'
import { linkText,heading } from './tags.module.css'
import Seo from '../components/seo'
const TagsPage = ({
data: {
allMdx: { group },
site: {
siteMetadata: { title },
},
},
}) => (
<Layout>
<h1 className={heading}>Tags</h1>
<ul>
{group.map(tag => (
<li key={tag.fieldValue}>
<Link to={`/tag/${kebabCase(tag.fieldValue)}/`} className={linkText}>
{tag.fieldValue} ({tag.totalCount})
</Link>
</li>
))}
</ul>
</Layout>
)
export const pageQuery = graphql`
query {
site {
siteMetadata {
title
}
}
allMdx(limit: 1000) {
group(field: { frontmatter: { tags: SELECT }}) {
fieldValue
totalCount
}
}
}
`
export const Head = () => <Seo title="Tags" />
export default TagsPage
これでタグ一覧のページが作成できました。
タグを選択するとそのタグが含まれたブログポストの一覧が表示されます。
ブログリストにタグを表示する
ブログページにタグを表示してリンクをつけます。下記のようにして日付の横にタグが表示されるようにしてみます。
import * as React from 'react'
import { Link, graphql } from 'gatsby'
import { GatsbyImage, getImage } from 'gatsby-plugin-image'
import Layout from '../components/layout'
import Seo from '../components/seo'
import Pager from '../components/pager'
import kebabCase from 'lodash/kebabCase'
const BlogPage = ({ data, pageContext }) => {
return (
<Layout pageTitle="Blog">
{
data.allMdx.nodes.map((node) => (
<article key={node.id}>
<h2>
<Link to={`/blog/${node.frontmatter.slug}`}>{node.frontmatter.title}</Link>
</h2>
<Link to={`/blog/${node.frontmatter.slug}`}>
<GatsbyImage
image={getImage(node.frontmatter.hero_image)}
alt={node.frontmatter.hero_image_alt}
/>
</Link>
<span>Posted: {node.frontmatter.date} </span>
<span>
Tags:
{node.frontmatter.tags.map(
(tag) => tag && (
<Link
to={`/tag/${kebabCase(tag)}`}>#{tag}
</Link>
)
)}
</span>
<p>{node.excerpt}</p>
</article>
))
}
<Pager pageContext={pageContext} />
</Layout>
)
}
export const query = graphql`
query($skip: Int!, $limit: Int!) {
allMdx(
sort: { frontmatter: { date: DESC } }
skip: $skip
limit: $limit
) {
nodes {
frontmatter {
title
date(formatString: "MMMM D, YYYY")
slug
tags
hero_image_alt
hero_image {
childImageSharp {
gatsbyImageData
}
}
}
id
excerpt
}
}
}
`
export const Head = () => <Seo title="Blog" />
export default BlogPage
kebabCaseを読みこみ、クエリにtagsを追加しています。表示を確認します。
これでブログリストにタグを追加できました。今回は以上です。