Gatsbyに前後の記事へのリンクを追加する方法

blog-hero-img本記事では最初にGatsbyサイトにブログ機能を追加し、次にNPMパッケージなどは使わずに前後の記事へ移動する機能を実装します。

pen-icon2021.11.28

Profile Pic

この記事の筆者:三好アキ(エンジニア)


ウェブデザイナーから『エンジニア』『プログラマー』へ成長したい人、独学で進んでいきたい人を応援しています。 HTMLとCSSの知識だけでアプリ開発を始められる入門書を多数執筆中📕📗👇


ウェブ制作の教本『はじめてつくるReactアプリ』など複数冊を執筆。



React、Next.js、TypeScriptなどのお役立ち情報や実践的コンテンツを、ビギナー向けにかみ砕いて配信中。登録はこちらから → 無料メルマガ登録

前後の記事へのリンク

Jamstackサイト制作でよく使われるGatsbyに、前後の記事へと移動する仕組みを追加します。

本記事は、まず最初にGatsbyサイトに最もベーシックなブログ機能を実装し、そこに前後の記事へのリンクを追加するという流れです。

ロジック部分の説明だけなので、スタイルは各自お好みで適用してください。

なお、本記事ではGatsbyの最新バージョン(2021年11月時点)であるv4を使いますが、v2でもv3でも本記事と同じコードで前後の記事へと移動する仕組みを実装できます。

なおページネーション機能は、こちらの記事を参考にしてください。

ベーシックなブログ機能の作成

まずGatsbyのスターターサイトに、マークダウンのブログ機能を追加しましょう。

最初にスターターサイトをダウンロードするので、ターミナルで以下のコマンドを実行します。

npx gatsby new gatsby-starter-default https://github.com/gatsbyjs/gatsby-starter-default

次にブログ記事を一覧表示するblog.js、個別記事を表示するsingleBlog.jsを作ります。

まずはblog.jsから始めます。


├──pages
│       ├── 404.js   
│       ├── blog.js    ←追加
│       ├── index.js  
│       ├── pages-2.js   
│       ├── using-ssr.js  
│       └── using-typescript.tsx 

次のコードを書きます。

// blog.js

import * as React from "react"
import { graphql, Link } from "gatsby"
 
const Blog = (props) => {
    return (
        <div>    
            <h1>ブログページ</h1>
            {props.data.allMarkdownRemark.edges.map((singleBlog, index) => 
                <div key={index}>              
                    <h2><Link to={`/blog${singleBlog.node.fields.slug}`}>{singleBlog.node.frontmatter.title}</Link></h2>       
                    <p>{singleBlog.node.frontmatter.date}</p>      
                </div>              
            )}  
        </div>  
    )
}

export default Blog

export const query = graphql`
    query BlogQuery { 
        allMarkdownRemark(
            sort: {fields: frontmatter___id, order: DESC}     
        ) {
            edges {
                node {
                    fields {
                        slug
                    }
                    frontmatter {
                        date
                        summary
                        id
                        title
                    }
                }
            }
        }
    }
`

singleBlog.jstemplatesフォルダ内に作ります。


├──templates 
│       ├── singleBlog.js    ←追加  
│       └── using-dsg.js

次のコードを書きます。

// singleBlog.js

import * as React from "react"
import { graphql } from "gatsby"
 
const SingleBlog = (props) => {
    return (
        <div>                       
            <h1>{props.data.markdownRemark.frontmatter.title}</h1> 
            <p>{props.data.markdownRemark.frontmatter.date}</p> 
            <div dangerouslySetInnerHTML={{ __html: props.data.markdownRemark.html }} />  
        </div>     
    )
}

export default SingleBlog

export const query = graphql`
    query SingleBlogQuery($slug: String!) {
        markdownRemark(fields: { slug: { eq: $slug } }) {
            frontmatter {
                date
                title
                summary
                id
            }
            html
        }
    }
`

次に、ブログ記事のマークダウンファイルを収納するフォルダをsrc内に作ります。


├──src
│  ├── components    
│  ├── data    ←追加
│  ├── images  
│  ├── pages   
│  └── templates    

data内には6つのマークダウンファイルを作ります。


├── data
│      ├── first.md    
│      ├── second.md  
│      ├── third.md   
│      ├── fourth.md  
│      ├── fifth.md   
│      └── sixth.md    

それぞれのマークダウンファイルには、下のように基本的な情報を書きます。

// first.md

---
id: "1"
title: "記事1"
date: "2021-06-21"
summary: "記事1の要約"
---

1つ目の記事。
// second.md

---
id: "2"
title: "記事2"
date: "2021-06-22"
summary: "記事2の要約"
---

2つ目の記事。
// third.md

---
id: "3"
title: "記事3"
date: "2021-06-23"
summary: "記事3の要約"
---

3つ目の記事。
// fourth.md

---
id: "4"
title: "記事4"
date: "2021-06-24"
summary: "記事4の要約"
---

4つ目の記事。
// fifth.md

---
id: "5"
title: "記事5"
date: "2021-06-25"
summary: "記事5の要約"
---

5つ目の記事。
// sixth.md

---
id: "6"
title: "記事6"
date: "2021-06-26"
summary: "記事6の要約"
---

6つ目の記事。

これでブログのデータと、それを表示する部分ができました。

次は表示するための仕組みづくりです。

ブログ機能に必要なプラグインをインストールします。

npm install gatsby-transformer-remark

これと、デフォルトですでに入っているgatsby-source-filesystemgatsby-config.jsに追加します。

// gatsby-config.js

module.exports = {
  siteMetadata: {
     
      ...

  plugins: [
    `gatsby-plugin-react-helmet`,
    `gatsby-plugin-image`,
    // ⬇ 追加
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `data`,
        path: `${__dirname}/src/data`,
      },
    },
    `gatsby-transformer-remark`,
    // ⬆ 追加
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    },
    `gatsby-transformer-sharp`,

    ...

gatsby-node.jsに、上で作ったコンポーネントにマークダウンファイルを流し込むためのコードを追加します。

// gatsby-node.js

// ⬇ 削除
exports.createPages = async ({ actions }) => {
  const { createPage } = actions
  createPage({
    path: "/using-dsg",
    component: require.resolve("./src/templates/using-dsg.js"),
    context: {},
    defer: true,
  })
}
// ⬆ 削除

const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)

exports.onCreateNode = ({ node, getNode, actions }) => {
    const { createNodeField } = actions
  
    if (node.internal.type === `MarkdownRemark`) {
        const slug = createFilePath({ node, getNode })   
        createNodeField({         
            node,                   
            name: `slug`,           
            value: slug,            
        })
    }
}

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

    const result = await graphql(`              
        query {
            allMarkdownRemark {
                edges {
                    node {
                        fields {
                            slug
                        }
                    }
                }
            }
        }
    `)
  
    result.data.allMarkdownRemark.edges.forEach(({ node }) => {
        createPage({
            path: `blog${node.fields.slug}`,
            component: path.resolve(`./src/templates/singleBlog.js`),
            context: {
                slug: node.fields.slug,
            },
        })
    })
}

ここまで加えた変更をすべて保存します。

gatsby developでGatsbyを起動し、http://localhost:8000/blogをブラウザで開くと、ブログ機能が追加されているのがわかります。

これ以降、本題の前後のページへと移動する機能を追加していきます。

実装に必要なもの3つ

前後の記事へのリンク実装には次の3つが必要です。


1 gatsby-node.jsに前後の記事の情報を取得するGraphQLクエリを追加

2 取得した前後の記事の情報をsingleBlog.jsコンポーネントに渡す

3 prevNext.jsコンポーネント


それでは順番通りに作っていきます。

gatsby-node.jsにGraphQLクエリを追加

次のコードをgatsby-node.jsに追加します。

// gatsby-node.js

const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)

exports.onCreateNode = ({ node, getNode, actions }) => {
    const { createNodeField } = actions
  
    if (node.internal.type === `MarkdownRemark`) {
        const slug = createFilePath({ node, getNode })   
        createNodeField({         
            node,                   
            name: `slug`,           
            value: slug,            
        })
    }
}

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

    const result = await graphql(`              
        query {
            allMarkdownRemark (sort: { order: ASC, fields: [frontmatter___id] }){    // 追加
                edges {
                    node {
                        fields {
                            slug
                        }
                    }
                    // ⬇ 追加
                    next {
                        frontmatter {
                            title
                        }
                        fields {
                            slug
                        }
                    }
                    previous {
                        fields {
                            slug
                        }
                        frontmatter {
                            title
                        }
                    }
                    // ⬆ 追加
                }
            }
        }
    `)
  
    result.data.allMarkdownRemark.edges.forEach(({ node, next, previous }) => {    // 追加
        createPage({
            path: `blog${node.fields.slug}`,
            component: path.resolve(`./src/templates/singleBlog.js`),
            context: {
                slug: node.fields.slug,
                next,       // 追加
                previous,   // 追加
            },
        })
    })
}

いま追加したコードは、まずGraphQLで前後の記事のタイトル(title)とパス情報(slug)を取得しています。

次にcontextの中にそれを置きます。

これによって、singleBlog.jsからpageContextによってそのデータにアクセスできます。

なおallMarkdownRemarkに追加したコードは、前後の記事の順番を整えるためのものです。         

前後の記事の情報をsingleBlog.jsに渡す

context内のデータはpageContextで渡されるので、次のコードを追加します。

// singleBlog.js

import * as React from "react"
import { graphql } from "gatsby"
import PrevNext  from "../components/prevNext"   // 追加
 
const SingleBlog = (props) => {
    return (
        <div>                       
            <h1>{props.data.markdownRemark.frontmatter.title}</h1> 
            <p>{props.data.markdownRemark.frontmatter.date}</p> 
            <div dangerouslySetInnerHTML={{ __html: props.data.markdownRemark.html }} />  
            <PrevNext pageContext={props.pageContext} />  // 追加
        </div>     
    )
}

export default SingleBlog

export const query = graphql`
    query SingleBlogQuery($slug: String!) {
        markdownRemark(fields: { slug: { eq: $slug } }) {
            frontmatter {
                date
                title
                summary
                id
            }
            html
        }
    }
`

最後に、いま追加したprevNext.jsコンポーネントを作ります。

prevNext.jsコンポーネント

prevNext.jscomponentsフォルダに作ります。

src
.
.
├── components    
│      ├── header.js
│      ├── layout.css
│      ├── layout.js  
│      ├── prevNext.js    ←追加    
│      └── seo.js
.
.

そこに次のコードを書きます。

// prevNext.js

import * as React from "react"
import { Link } from 'gatsby'

const PrevNext =({ pageContext }) => {
    const { previous, next } = pageContext
    return (
        <div>
            {previous && 
                <Link to={`/blog${previous.fields.slug}`}>
                    <h3>{previous.frontmatter.title}</h3>
                </Link>
            }
            {next && 
                <Link to={`/blog${next.fields.slug}`}>
                    <h3>{next.frontmatter.title}</h3>
                </Link>
            }
        </div>
    )
}

export default PrevNext

変更を保存してgatsby developでGatsbyを起動させ、ブラウザでhttp://localhost:8000/blogに表示されている記事を開くと、前後の記事へと移動する機能が追加されているのがわかります。

本記事では細かい説明を省略して足早に解説しましたが、Gatsbyについてよりくわしく知りたい方は、拙著「はじめてつくるGatsbyサイト」を参考にしてください。

なお、Gatsbyブログの記事一覧ページ(blog.js)に表示される記事数を制限するページネーション機能については、こちらの記事を参考にしてください。

その他、Gatsbyに興味のある人は次の記事も参考にしてください。