GatsbyJS 記事のグループ化

GatsbyJS 記事のグループ化。Tagsを実装。Categoryなども同様

tags 追加

Frontmatter に tag 追加

---
title: Hello World
date: "2015-05-01T22:12:03.284Z"
description: "Hello World"
tags: [ "tag1", "tag2", "tag3" ]
---

allMarkdownRemark の node に tags 追加

src/gatsby-node/index.ts

// Get all markdown blog posts sorted by date
const result: any = await graphql(
  `
    {
      allMarkdownRemark(sort: { fields: [frontmatter___date], order: ASC }, limit: 1000) {
        nodes {
          id
          fields {
            slug
          }
          frontmatter {
            tags
          }
        }
      }
    }
  `
)

Post 一覧の項目に Tags を追加

graphql に tags 追加

src/pages/index.tsx

import kebabCase from "lodash/kebabCase"

src/pages/index.tsx

export const pageQuery = graphql`
  query Index {
    site {
      siteMetadata {
        title
      }
    }
    allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
      nodes {
        excerpt
        fields {
          slug
        }
        frontmatter {
          date(formatString: "MMMM DD, YYYY")
          title
          description
          tags
        }
      }
    }
  }
`

日付の下辺りに

src/pages/index.tsx

<ul className={"tags"}>
  {post?.frontmatter?.tags?.map(tag => {
    return (
      <li key={tag}>
        <Link to={`/tags/${kebabCase(tag)}/`}>{tag}</Link>
      </li>
    )
  })}
</ul>

スクリーンショット

リンク先がないのでリンク先のページを生成

template 作成

src/templates/tags.tsx

import React from "react"
import Bio from "../components/bio"
import {Link, graphql} from "gatsby"
import Layout from "../components/layout"
import Seo from "../components/seo"

const Tags = ({pageContext, data}: { pageContext: any; data: any }) => {
  const siteTitle = data.site?.siteMetadata?.title || `Title`
  const {tag} = pageContext
  const posts = data.allMarkdownRemark.nodes

  return (
    <Layout location={location} title={siteTitle}>
      <Seo title="All posts"/>
      <Bio/>
      <h1>Tagging: {tag}</h1>
      <ol style={{listStyle: `none`}}>
        {posts.map((post: any) => {
          const title = post.frontmatter?.title || post.fields?.slug

          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>{post.frontmatter?.date || ""}</small>
                </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 Tags($tag: String) {
    site {
      siteMetadata {
        title
      }
    }
    allMarkdownRemark(
      sort: { fields: [frontmatter___date], order: DESC }
      filter: { frontmatter: { tags: { in: [$tag] } } }
    ) {
      totalCount
      nodes {
        fields {
          slug
        }
        frontmatter {
          date(formatString: "MMMM DD, YYYY")
          title
          description
          tags
        }
      }
    }
  }
`

page 生成

src/gatsby-node/index.ts

const _ = require("lodash")


export const createPages: GatsbyNode["createPages"] = async ({graphql, actions, reporter}) => {
...
...
  ここに下記を追加
...
...
}

src/gatsby-node/index.ts

////////////////////////////////////////////////
///  Tags
////////////////////////////////////////////////
// console.log(posts)
let tags = posts.reduce((tags: any, node: any) => {
  const nodesTags: any = node?.frontmatter?.tags
  return nodesTags ? tags.concat(nodesTags) : tags
}, [])
// 重複削除
tags = [...new Set(tags)]
console.log(tags)

const tagTemplate = path.resolve("src/templates/tags.tsx")

tags.forEach((item: any) => {
  const tag = item
  const prefix = `/tags/${_.kebabCase(tag)}/`
  createPage({
    path: prefix,
    component: tagTemplate,
    context: {
      tag: tag,
    },
  })
})

tagsのリンク

tag のリンクをクリック

tagsページ

このページにもindex.tsxのように各 Post のタグを表示する。
ページはsrc/pages/index.tsx,templates/tags.tsx と別なのだがやることは同じなので component 化する。

src/pages/index.tsxに追記した下記を

<ul className={"tags"}>
  {post?.frontmatter?.tags?.map(tag => {
    return (
      <li key={tag}>
        <Link to={`/tags/${kebabCase(tag)}/`}>{tag}</Link>
      </li>
    )
  })}
</ul>

src/compnents/tagsInlist.tsx にコピー

src/compnents/tagsInlist.tsx

import * as React from "react"
import {Link} from "gatsby"
import kebabCase from "lodash/kebabCase"
import styled from "styled-components"

const TagsInList = ({tags}: { tags: any }) => {
  return (
    <UlWrapper className={"tags"}>
      {tags?.map((tag: string) => {
        return (
          <li key={tag}>
            <Link to={`/tags/${kebabCase(tag)}/`}>{tag}</Link>
          </li>
        )
      })}
    </UlWrapper>
  )
}

export default TagsInList

src/templates/tags.tsx を編集

  • import component
  • component 配置
  • component へ tags を props

を追加

import React from "react"
import Bio from "../components/bio"
import {Link, graphql} from "gatsby"
import Layout from "../components/layout"
import Seo from "../components/seo"
import TagsInList from "../components/tagsInList"

const Tags = ({pageContext, data}: { pageContext: any; data: any }) => {
  const siteTitle = data.site?.siteMetadata?.title || `Title`
  const {tag} = pageContext
  const posts = data.allMarkdownRemark.nodes

  return (
    <Layout location={location} title={siteTitle}>
      <Seo title="All posts"/>
      <Bio/>
      <h1>Tagging: {tag}</h1>
      <ol style={{listStyle: `none`}}>
        {posts.map((post: any) => {
          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>{post.frontmatter?.date || ""}</small>
                </header>
                <TagsInList tags={tags}/>
                <section>
                  <p
                    dangerouslySetInnerHTML={{
                      __html: post.frontmatter?.description || post?.excerpt || "",
                    }}
                    itemProp="description"
                  />
                </section>
              </article>
            </li>
          )
        })}
      </ol>
    </Layout>
  )
}
export default Tags

export const pageQuery = graphql`
  query Tags($tag: String) {
    site {
      siteMetadata {
        title
      }
    }
    allMarkdownRemark(
      sort: { fields: [frontmatter___date], order: DESC }
      filter: { frontmatter: { tags: { in: [$tag] } } }
    ) {
      totalCount
      nodes {
        fields {
          slug
        }
        frontmatter {
          date(formatString: "MMMM DD, YYYY")
          title
          description
          tags
        }
      }
    }
  }
`

component を インポート

src/pages/index.tsx

import TagsInList from "../components/tagsInList"

.....
.....
.....
<TagsInList tags={post?.frontmatter?.tags}/>
.....
.....
.....

src/templates/tags.tsx

import TagsInList from "../components/tagsInList"

.....
.....
.....
<TagsInList tags={node?.frontmatter?.tags}/>
.....
.....
.....

TagsInList


おっさんWEBエンジニア奮闘記©2007 WEBDIMENSION