2019-10-08に投稿

GatsbyJSで次の記事、前の記事へのリンクを生成する方法

オリジナルの記事はこちら


こんにちは。mono(@mono7555e)です。

記事ページで他の記事へ行く術がなく直帰率が高くなっていたので他の記事への遷移を追加することにしました。

ちょっとひと手間かかりますがGatsbyJSでも実現できましたのでご紹介したいと思います。

ページ生成時に前後の記事情報を渡す

exports.createPages = async ({ actions, graphql }) => {
  const { createPage } = actions
  const template = path.resolve(`src/templates/blog.tsx`)
  await graphql(`
    {
      allMarkdownRemark(
        sort: { order: DESC, fields: [frontmatter___published_at] }
        limit: 2000
      ) {
        edges {
          node {
            fields {
              slug
            }
            frontmatter {
              title
              image
            }
          }
        }
      }
    }
  `).then(result => {
    if (result.errors) {
      return Promise.reject(result.errors)
    }
    const pages = result.data.allMarkdownRemark.edges
    pages.forEach(({ node }, index) => {
      const prev = index === 0 ? null : pages[index - 1].node
      const next = index === pages.length - 1 ? null : pages[index + 1].node
      createPage({
        path: node.fields.slug,
        component: template,
        context: {
          slug: node.fields.slug,
          prev,
          next,
        },
      })
    })
  })
}

重要なのはconst prev = ~const next ~の部分と、contextでそれを設定している部分です。
それ以外のところは自由です。

設定された前後の記事情報を元にリンクを生成

ちょっとごちゃごちゃしてしまったので前後の記事用のコンポーネントに分割しました。

import React from "react"
import { Box, Card, CardActionArea, CardContent, Typography } from "@material-ui/core"

import Image from "../atoms/image"

const PrevNext = ({ prev, next }) => {
  return(
    <Box mt={5} display="flex" justifyContent="space-between">
      { prev == null ? null : _link(prev, `prev`)}
      { next == null ? null : _link(next, `next`)}
    </Box>
  )
}
export default PrevNext

const _link = ({ fields, frontmatter }, direction) =>{
  return(
    <Box display="flex" flexDirection="column" style={{ width: `40%` }}>
      <Typography gutterBottom variant="body2" component="h6">
        { direction == 'prev' ? '前の記事' : '次の記事'}
      </Typography>
      <Card style={{ width: `100%`, height: `100%`}}>
        <CardActionArea href={fields.slug} style={{ display: 'flex', alignItems: `stretch`, height: `100%` }}>
          <Box style={{ width: `35%` }}>
            <Image filename={frontmatter.image} style={{ height: `100%` }} />
          </Box>
          <CardContent style={{ flex: `1` }}>
            <Typography variant="body2" component="p">
              {frontmatter.title}
            </Typography>
          </CardContent>
        </CardActionArea>
      </Card>
    </Box>
  )
}

このブログはMaterial-UIを使っているのでちょっとややこしい感じになりますが、単純にリンクをするだけであればもう少し簡単に書けると思います。

あとは作った前後の記事用のコンポーネントをテンプレートから呼び出すだけです。

// importなどなど
export default function Template({ data, pageContext }) {
  // 中略
  return (
    <Layout>
      <Container maxWidth="md" component="article">
        {/* その他コンポーネント読み込み */}
        <PrevNext prev={pageContext.prev} next={pageContext.next} />
      </Container>
    </Layout>
  )
}

まとめ

やってみると結構かんたんでした。

今はイメージとタイトルだけですが、タグや日付など各記事が持っている情報を利用できるのでよりリッチな前後の記事へのリンクが設置できると思います。

Originally published at www.mono7555e.com
ツイッターでシェア
みんなに共有、忘れないようにメモ

mono7555e

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント