Gatsby にキーワードページを導入する

投稿した記事にタグ付けするような機能は是非とも欲しいと思っていた。こちらもページネーションの記事同様、実装がさほど難しくなさそうだったのでプラグインではなく自前で実装した。

用意するもの

  • キーワード一覧ページ
  • キーワードに紐づく記事一覧ページ
    • レイアウト
    • URL

基本的にやることや用意するものはページネーションの記事と同じ。内部的に発行する GraphQL が多少違うだけ。

設計

Markdown内のキーワードの指定の仕方
---
title: タイトル
date: 2021-03-26
keywords:
    - keyword_1
    - keyword_2
---

## ヘッダ

ここから記事本文

各記事のメタデータ記載部分に keywords という項目を作ることで記事にキーワードを付与する事ができるようにする。

キーワード一覧ページを作る

キーワード一覧ページは表示するコンテンツによって URL が変わったりしないためページコンポーネントとして作成した。

pages/keywords.js
import React from "react"
import { Link, graphql } from "gatsby"

import GlobalLayout from "../layouts/global-layout"
import SEO from "../components/seo"


const Keywords = ({data}) => {
  return (
    <GlobalLayout>
      <SEO title="Keyword List" />
      <h3 className="page-label">Keyword list</h3>
      <ul className="keyword-list">
        {data.allMdx.group.map(({fieldValue, totalCount}, idx) => {
          return (
            <li key={idx}>
              <Link to={`/keyword/${fieldValue}/`}>
                {fieldValue}: {totalCount}
              </Link>
            </li>
          )
        })}
      </ul>
    </GlobalLayout>
  )
}

export const query = graphql`
  {
    allMdx {
      group(field: frontmatter___keywords) {
        fieldValue
        totalCount
      }
    }
  }
`


export default Keywords

キーワードに紐づく記事一覧ページ

キーワード一覧ページが出来たら、今度はそのページから遷移する先の「選択されたキーワードに紐づく記事一覧ページ」が必要となる。

レイアウト

こちらは選択されたキーワードによって URL が変わるため gatsby-node.js で URL を作らないといけない。その際にレイアウトファイルを結びつける必要があるため先にレイアウトファイルを作る。

PostExcerpt コンポーネントは記事の見出し用コンポーネントで、トップページやページネーション内でも使っているもの。

layouts/keyword-page-layout.js
import React from "react"
import {graphql} from "gatsby"

import GlobalLayout from "./global-layout"
import SEO from "../components/seo"
import PostExcerpt from "../components/post-excerpt"

const KeywordPageLayout = ({data, pageContext}) => {
  const pageTitle = `Keyword: "${pageContext.keyword}"`
  return (
    <GlobalLayout>
      <SEO title={pageTitle} />
      <h3 className="page-label">{pageTitle}</h3>
      {data.allMdx.edges.map(({node}, idx) => {
        return (
          <PostExcerpt
            key={idx}
            createdDate={node.frontmatter.date}
            name={node.slug}
            title={node.frontmatter.title}
            excerpt={node.excerpt}
          />
        )
      })}
    </GlobalLayout>
  )
}

export const query = graphql`
query KeywordPageLayoutQuery($keyword: String) {
  allMdx(
    filter: {frontmatter: {keywords: {eq: $keyword}}}
    sort: {fields: frontmatter___date, order: DESC}
  ) {
    edges {
      node {
        frontmatter {
          title
          date
        }
        slug
        excerpt
      }
    }
  }
}
`

export default KeywordPageLayout

URL 作成

つづいて URL 作成。一部抜粋したものだが下記のような感じ。これにより /keyword/<キーワード>/ という URL に対し、上で作った layouts/keyword-page-layout.js が対応してくれるようになる。

gatsby-node.js
exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions
  await createKeywordPages(graphql, createPage)
}

async function createKeywordPages(graphql, createPage) {
  const result = await graphql(`
    {
      allMdx {
        distinct(field: frontmatter___keywords)
      }
    }
  `)
  result.data.allMdx.distinct.forEach((keyword) => {
    createPage({
      path: `/keyword/${keyword}/`,
      component: path.resolve('./src/layouts/keyword-page-layout.js'),
      context: {
        keyword: keyword,
      }
    })
  })
}