GatsbyJSブログにタグページを追加した

2020-05-04

はじめに

おはようございます!こんにちは!こんばんは!
麻雀と芝生大好きおじさんことのふのふ(@rpf_nob)です!!

今回はGatsbyJSブログにタグページを追加して、タグごとに記事一覧が表示できるようにします。
基本的には公式ページの内容通りやればできます。

前提

このブログはGatsbyJSのgatsby-starter-blogのテンプレートから作成しています。

記事のマークダウンファイルにタグを追加する

各記事のマークダウンファイルの上部のメタ情報の部分に[tags]を追加します。複数ある場合は[,]で連結して記載します。

---
title: GatsbyJSブログにタグページを追加した
date: "2020-05-04"
description: 今回はGatsbyJSブログにタグページを追加して、タグごとに記事一覧が表示できるようにします。
slug: 2020-05-04/gatsby-tag
tags: [GatsbyJS]---

GraphQLのクエリを記述して投稿記事のタグを全て取得する

GraphiQL(http://localhost:8000/___graphql)にアクセスして次のクエリを実行すれば、 下のような結果が返ってくるはずです。投稿記事を[tags]でグループ化して、各tagの投稿数が[totalCount]として返ってきます。

GraphiQL
{
  allMarkdownRemark {
    group(field: frontmatter___tags) {
      tag: fieldValue
      totalCount
    }
  }
}
{
  "data": {
    "allMarkdownRemark": {
      "group": [
        {
          "tag": "GatsbyJS",
          "totalCount": 5
        },
        {
          "tag": "Netlify",
          "totalCount": 1
        },
        {
          "tag": "エンジニア",
          "totalCount": 1
        },
        {
          "tag": "コロナ",
          "totalCount": 1
        },
        {
          "tag": "テレワーク",
          "totalCount": 1
        },
        {
          "tag": "肥料",
          "totalCount": 1
        },
        {
          "tag": "芝生",
          "totalCount": 1
        }
      ]
    }
  }
}

タグページのテンプレートを作成する

[src/templates/tags.js]を新規作成し、タグページのテンプレートを作成していきます。
基本的にはトップページと同じような見た目にしたいので、ソースコードもほとんど同じになります。
コンポーネント化して共通化したいですね・・・

src/templates/tags.js
import React from "react";

import Bio from "../components/bio";
import Layout from "../components/layout";
import SEO from "../components/seo";
import { rhythm } from "../utils/typography";

// Components
import { Link, graphql } from "gatsby";

const Tags = ({ pageContext, data, location }) => {
  const { tag } = pageContext;
  const { edges, totalCount } = data.allMarkdownRemark;
  const author = data.site.siteMetadata.author.name;

  const tagHeader = `[${tag}]タグの記事一覧(全${totalCount}件)`;

  return (
    <div>
      <Layout location={location} author={author}>
        <SEO
          title={`Tag: ${tag}`}
          description={`${tag}タグを含む記事の一覧ページです`}
        />
        <Bio />
        <h2>{tagHeader}</h2>
        {edges.map(({ node }) => {
          const title = node.frontmatter.title || node.fields.slug;
          return (
            <article key={node.fields.slug}>
              <header>
                <h3
                  style={{
                    marginBottom: rhythm(1 / 4),
                  }}
                >
                  <Link
                    style={{ boxShadow: `none` }}
                    to={node.frontmatter.slug}
                  >
                    {title}
                  </Link>
                </h3>
                <small>{node.frontmatter.date}</small>
              </header>
              <section>
                <p
                  dangerouslySetInnerHTML={{
                    __html: node.frontmatter.description || node.excerpt,
                  }}
                />
              </section>
            </article>
          );
        })}
      </Layout>
    </div>
  );
};

export default Tags;

export const pageQuery = graphql`
  query($tag: String) {
    site {
      siteMetadata {
        title
        author {
          name
        }
      }
    }
    allMarkdownRemark(
      limit: 2000
      sort: { fields: [frontmatter___date], order: DESC }
      filter: { frontmatter: { tags: { in: [$tag] } } }
    ) {
      totalCount
      edges {
        node {
          fields {
            slug
          }
          frontmatter {
            title
            date(formatString: "YYYY-MM-DD")
            description
            slug
          }
        }
      }
    }
  }
`;

ページをレンダリングする

[gatsby-node.js]に↑で作成したテンプレートを使用して、ページをレンダリングするように変更します。
以下のように変更すれば、[/tags/タグ名]に各タグの記事一覧ページが作成されます。

gatsby-node.js
const path = require(`path`);
const _ = require("lodash");

exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions;

  const blogPost = path.resolve(`./src/templates/blog-post.js`);
  const tagTemplate = path.resolve(`./src/templates/tags.js`);  const result = await graphql(
    `      {
        posts: allMarkdownRemark(
          sort: { fields: [frontmatter___date], order: DESC }
          limit: 1000
        ) {
          edges {
            node {
              fields {
                slug
              }
              frontmatter {
                title
                slug
              }
            }
          }
        }
        tags: allMarkdownRemark(limit: 1000) {
          group(field: frontmatter___tags) {
            fieldValue
          }
        }
      }
    `
  );
  if (result.errors) {
    throw result.errors;
  }

  const posts = result.data.posts.edges;

  posts.forEach((post, index) => {
    const previous = index === posts.length - 1 ? null : posts[index + 1].node;
    const next = index === 0 ? null : posts[index - 1].node;

    createPage({
      path: `/${post.node.frontmatter.slug}/`,
      component: blogPost,
      context: {
        slug: post.node.fields.slug,
        previous,
        next,
      },
    });
  });
  const tags = result.data.tags.group;  tags.forEach(tag => {    createPage({      path: `/tags/${_.kebabCase(tag.fieldValue)}/`,      component: tagTemplate,      context: {        tag: tag.fieldValue,      },    });  });};

これで以下のようなタグページが作成されます。 img1.png

記事にタグページジャンプ用のタグをつける

最後に記事ページにタグページへのジャンプするためのタグをつけていきます。

タグ表示用のコンポーネントを作成する

propsで記事のタグのリストを渡してもらい、それを使ってタグページにジャンプできるようにします。

src/components/tag.js
import React from "react";
import { Link } from "gatsby";
import _ from "lodash";

const Tag = props => {
  return (
    <div className="tag">
      {props.tags.map((tag, index) => {
        return (
          <Link
            to={`/tags/${_.kebabCase(tag)}/`}
            key={index}
            className="tag__list"
          >
            {tag}
          </Link>
        );
      })}
    </div>
  );
};

export default Tag;

作成したタグコンポーネントを目次の前に挿入

[src/templates/blog-post.js]の目次の前に上で作ったTagコンポーネントを挿入します。
GraphQLで取得したdata.frontmatter.tagsをpropsとして渡します。

src/templates/blog-post.js
<header>
・・・省略
</header>
<Tag tags={post.frontmatter.tags} />
<Toc data={data.markdownRemark.tableOfContents} />
<section/>

スタイル調整

あとは好きなスタイルを付けて完成となります。

src/styles/style.scss
.tag {
  margin-bottom: 20px;

  &__list {
    margin-right: 10px;
    box-shadow: none;
    border: 1px solid #555;
    padding: 0 5px;
    border-radius: 3px;
    color: blue;
    background-color: #f2f2f2;
  }
}

img2.png

まとめ

今回はGatsbyJSブログにタグページを追加して、タグごとに記事一覧が表示できるようにしました。
記事が増えてきたときに、タグページがあると読みたいタグを一気に見れるのでいいですよね。

他にもGatsbyJSのブログカスタマイズをいろいろやっているので、以下もあわせてご覧いただければと思います。



最後まで見ていただきありがとうございます!!


Written by のふのふ

東京で働く名古屋生まれの麻雀と芝生と娘と妻を愛するアラフォーエンジニアです。 フロントエンドよりのweb開発技術(React,TypeScriptなど)を中心に扱っています。 web開発やデータサイエンス周りの技術に興味があります。 個人開発でwebアプリをリリースすることを目標にしています。

©2020 のふのふ All Rights Reserved.