Gatsby.js - 記事ページの作成と前後記事へのリンク(Markdown)

1. プラグインをインストール

はじめにマークダウンで記事ページを作成するために必要なプラグインをインストールします。

gatsby-source-filesystem
ローカルから Gatsby アプリケーションにデータを読み込むために必要なプラグイン

gatsby-transformer-remark
Markdown ファイルを使えるようにするためのプラグイン

プラグインをインストールする

//npm
npm install gatsby-source-filesystem gatsby-transformer-remark

//yarn
yarn add gatsby-source-filesystem gatsby-transformer-remark

2. プラグインの設定

プラグインをインストールしたら、プラグインの設定をgatsby-config.jsに記述します。${__dirname}/content/blogの部分がマークダウンファイルを入れるパスになっているのでマークダウンファイルの保存場所を変更したい場合は、このパスを変更します。

//gatsby-config.js
module.exports = {
  /* Your site config here */
  plugins: [
    //start
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `posts`,
        path: `${__dirname}/content/blog`, //パス
      },
    },
    //end
    `gatsby-transformer-remark`,
  ],
}

3. 必要なフォルダとファイルを用意

記事ページ作成に必要なフォルダやファイルを用意します。

  • gatsby-node.js
  • src > templates > blog-post-temp.js(記事ページを表示するファイル)
  • content > blog > article1 > index.md(マークダウンファイル)

index.md(マークダウンファイル)には下記を記述します。

---
title: article1
date: "2000-01-01"
---

article1

4. 記事ページを作成する

記事ページを作成するためにgatsby-node.jsに下記を記述します。

//gatsby-node.js
const path = require(`path`)
const _ = require("lodash")
const { createFilePath } = require(`gatsby-source-filesystem`)

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

  const blogPostTemp = path.resolve("src/templates/blog-post-temp.js") //パス

  const result = await graphql(`
    query {
      allMarkdownRemark(sort: { order: DESC, fields: [frontmatter___date] }) {
        edges {
          node {
            id
            fields {
              slug
            }
          }
        }
      }
    }
  `)

  //エラー発生時
  if (result.errors) {
    reporter.panicOnBuild(`result.errors`)
    return
  }

  //記事ページ作成
  const posts = result.data.allMarkdownRemark.edges

  posts.forEach(post => {
    createPage({
      path: post.node.fields.slug,
      component: blogPostTemp,
      context: {
        slug: post.node.fields.slug,
      },
    })
  })
}
//end

//field > slug作成
exports.onCreateNode = ({ node, getNode, actions }) => {
  const { createNodeField } = actions

  if (node.internal.type === `MarkdownRemark`) {
    const value = createFilePath({ node, getNode })

    createNodeField({
      name: `slug`,
      node,
      value,
    })
  }
}
//end

次にblog-post-temp.jsに下記を記述します。ここではlayout.jsを使用しているので作成していない場合は、<Layout>の部分を<div>に変更するか、Gatsby.js でサイトを作るとき最初に作っておきたいファイルページを参考にしてみて下さい。

//blog-post-temp.js
import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout"

const BlogPost = ({ data }) => {
  const post = data.markdownRemark
  return (
    <Layout>
      <h1>{post.frontmatter.title}</h1>
      <time>{post.frontmatter.date}</time>
      <div dangerouslySetInnerHTML={{ __html: post.html }} />
    </Layout>
  )
}
export default BlogPost

export const pageQuery = graphql`
  query BlogPostSlug($slug: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      html
      frontmatter {
        title
        date(formatString: "YYYY/MM/DD")
      }
    }
  }
`

これで記事ページが作成されていると思うのでgatsby developで一度確認します。開発サーバーを立ち上げて URL にhttp://localhost:8000/xなど存在しないページの URL を入力すると**development 404 page(すべてのページへのリンクが表示されるページ)**へ行けるので記事ページが作成されているかは、そのページで確認できます。

5. URL を変更したい場合

上記のままだと、記事ページはxxxx.com/article1/(マークダウンファイルを入れているフォルダ名がスラッグになります)のような URL になります。これをxxxx.com/blog/article1/のように変更したい場合は、gatsby-node.jsを下記のように変更します。

//gatsby-node.js

//記事ページ生成
const posts = result.data.allMarkdownRemark.edges

posts.forEach(post => {
  createPage({
    path: `/blog${post.node.fields.slug}`, //変更
    component: blogPostTemp,
    context: {
      slug: post.node.fields.slug,
    },
  })
})

gatsby-node.jsを変更したら記事一覧など、記事へのリンクも下記のように変更します。

<Link to={`/blog${node.fields.slug}`}>
  <h2>{node.frontmatter.title}</h2>
  <span>{node.frontmatter.date}</span>
</Link>

6. 前後の記事へのリンクを作成する

記事ページに前後の記事へのリンクを作成するために、記事が一つだけだと機能しないのでマークダウンファイルをもう一つ作成します。ここでは「article2」というファルダを作成し、下記の md ファイルを入れます。

---
title: article2
date: "2000-01-01"
---

article 2

gatsby-node.jsの記事ページ作成の部分を下記のように書き換えます。

//gatsby-node.js

//記事ページ生成
const posts = result.data.allMarkdownRemark.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.fields.slug,
    component: blogPostTemp,
    context: {
      slug: post.node.fields.slug,
      previous, //追加
      next, //追加
    },
  })
})

条件式 ? A : B - 条件式の値が true だった場合に A を返し、 false だった場合に B を返す

記事ページも下記のように書き換えると前後の記事へのリンクは完成です。

//blog-post-temp.js
import React from "react"
import { graphql, Link } from "gatsby" //Link追加
import Layout from "../components/layout"

const BlogPost = ({ data, pageContext }) => {
  const post = data.markdownRemark
  const { previous, next } = pageContext //追加

  return (
    <Layout>
      <h1>{post.frontmatter.title}</h1>
      <time>{post.frontmatter.date}</time>
      <div dangerouslySetInnerHTML={{ __html: post.html }} />

      {previous && (
        <Link to={previous.fields.slug} rel="prev">
          &laquo; Prev
        </Link>
      )}
      {next && (
        <Link to={next.fields.slug} rel="next">
          Next &raquo;
        </Link>
      )}
    </Layout>
  )
}
export default BlogPost

export const pageQuery = graphql`
  query BlogPostSlug($slug: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      html
      frontmatter {
        title
        date(formatString: "YYYY/MM/DD")
      }
    }
  }
`

前後記事のタイトルをリンクにしたい場合下記のようにするとタイトルが表示されます。

{
  next && (
    <Link to={next.fields.slug} rel="next">
      {next.frontmatter.title}
    </Link>
  )
}