【GatsbyJS × Contentful】記事内の画像を表示する

概要

gatsby-remark-images-contentfulと@contentful/rich-text-react-rendererを使用して、記事内の画像を表示する方法を紹介します。

静的サイトジェネレーターとは

WordPressなどの従来の方式では、ブラウザが新しいページの表示を要求するたびに、DBから表示したい内容を読み込んで返す方法を取っていました。

IMG 5461

静的サイトジェネレーター(SSG)ではブラウザからの要求に対して、あらかじめビルド(生成)しておいたhtmlファイルを返すことで、サイトの読み込み時間の短縮を図ります。

IMG 5462

前提

以下の手順が完了している前提で話を進めます。

  •  Contentfulアカウントが存在する
  •  CDA tokenを発行済(※1)
  •  Content ModelにContent Type:Rich TextのModelが存在する
  •  1つでも記事(Entry)が存在する
  •  GatsbyJSの開発環境を構築済み

※1:Content Delivery API (CDA)

gatsby-remark-images-contentful

Contentfulのマークダウン本文中の画像を表示してくれるパッケージです。 https://www.npmjs.com/package/gatsby-remark-images-contentful/

Contentfulに登録した画像から、ぼかして容量を軽くした画像が生成され、本来の画像が全て表示されるまでの間、代わりに生成済の画像を表示と説明されています。 これはMediumやFacebookでも使われてる技術なんだとか。

お使いの環境に合わせて、だいたいこんな感じで設定します。

plugins: [
  'gatsby-plugin-styled-components',
  'gatsby-plugin-react-helmet',
  {
    resolve: `gatsby-transformer-remark`,
    options: {
      plugins: [
        {
          resolve: `gatsby-remark-images-contentful`,
          options: {
            maxWidth: 650,
            backgroundColor: 'white',
            linkImagesToOriginal: false,
            withWebp: true,
          },
        },
      ],
    },
  },
]
import React from 'react'
import { graphql } from 'gatsby'

const PostTemplate = ({ data }) => {
  const {
    body
  } = data.contentfulPost

  return (
    <Layout>
      <PageBody body={body} />
    </Layout>
  )
}

export const query = graphql`
  query($slug: String!) {
    contentfulPost(slug: { eq: $slug }) {
      ...(中略) 
      body {
        childMarkdownRemark {
          html
        }
      }
    }
  }
`

IMB alDCSP

描画遅っ。 どうやらGatsbyJSの魅力的な機能である、「画像の最適化」が効いていないようです。

画像の最適化

表示する画像のサイズが大きすぎたり、読み込む画像数が多すぎると、サイトのパフォーマンスが低くなり、読み込み速度が低下します。 そんなときに役に立つのがgatsby-imageです。

  • 各デバイスのサイズと画面解像度に合った画像を読み込む。
  • 画像を読み込む間にサイズの小さなの画像(ぼかし効果の入った画像)を代わりに表示。
  • 画像を遅延して読み込むことで、初期読み込み時間を短縮。
  • ファイルサイズが小さくなるWebP画像を使用。(ブラウザがサポートしている場合のみ)

このようにサイトの読み込みを爆速にする仕組みが公式から提供されています。

(参考:https://using-gatsby-image.gatsbyjs.org

(gatsby-remark-images-contentfulでもできるはずなのに)

@contentful/rich-text-react-renderer

使用するパッケージを変えて試してみます。 @contentful/rich-text-react-renderer - npm

こんなイメージで設定します。

import React from 'react'
import { graphql } from 'gatsby'
import { documentToReactComponents } from "@contentful/rich-text-react-renderer"
import { BLOCKS } from "@contentful/rich-text-types"
import useContentfulImage from "../utils/useContentfulImage"

const options = {
  renderNode: {
    [BLOCKS.EMBEDDED_ASSET]: node => (
      <Img
        fluid={useContentfulImage(node.data.target.fields.file["ja-JP"].url)}
        alt={
          node.data.target.fields.description
            ? node.data.target.fields.description["ja-JP"]
            : node.data.target.fields.title["ja-JP"]
        }
      />
    ),
  },
}

export default ({ data }) => (
  <Layout>
   <div className="postbody">
          {documentToReactComponents(
            data.contentfulBlogPost.content.json,
            options
          )}
        </div>
  </Layout>
)

export const query = graphql`
  query($id: String!) {
    contentfulBlogPost(id: { eq: $id }) {
      content {
        json
      }
    }
  }
`

gatsby-source-contentful(Contentful公式Gatsbyパッケージ)が最近v4.0に更新され、リッチテキストのオブジェクト構造と名前が変更されました。

gatsby/CHANGELOG.md at master · gatsbyjs/gatsby · GitHub

version4系でなんとかできないか粘ってみたのですが、Cotentful公式にもほしい情報が載ってないため、version3の最新版を入れることにしました。(もうversion5も出てるのに悲しい。)

$ npm install gatsby-source-contentful@3.1.3

IMB A8AhsI

ぼやけた画像のあとで、本来Contentfulに登録していた画像が表示されることを確認できました。 フッターの背景画像も地味に最適化されています。別の箇所で同じような設定をしています。 (参考: https://github.com/ebisucom/gatsbyjs-book


画像の最適化によりサクッと読み込めるようになりました。ユーザー思いのとてもいい仕組みなので、広く普及してほしいです。もっと簡単に使えるようになるといいなー。