okaryo.log

gatsby-plugin-satorareを使ってGatsbyブログのOG画像をJSX構文で記事ごとに生成してみた | okaryo.log

gatsby-plugin-satorareを使ってGatsbyブログのOG画像をJSX構文で記事ごとに生成してみた

    #Gatsby#個人開発

(gatsby-plugin-satorareの開発者によるステマ記事です)

はじめに

最近gatsby-plugin-satorareというGatsbyプラグインが公開された。このプラグインでは内部でvercel/satoriが使用されており、OG画像のテンプレートをJSX構文で記述して作成できるようだ。

今回はこのプラグインを使って記事ごとのOG画像を作成してみる。

完成物

作成したOG画像を最初に紹介しておく。

これが記事用の画像。

記事用のOG画像

記事用とは別にサイトのOG画像も作成できるので以下のようなものを作成した。

サイトのOG画像

作り方

パッケージをインストール

まずはインストール。現時点での最新バージョンは0.1.1だ。

npm i -D gatsby-plugin-satorare

パッケージの設定

gatsby-config.js内にパッケージの設定を記述する。必須となる設定はpathのみで、ここにOG画像の元となるJSX/TSXファイルのパスを設定する。

その他の設定としては、画像のサイズやフォントなどが設定可能だ。詳しくはREADMEに記載があるので参照されたい。

{
  resolve: `gatsby-plugin-satorare`,
  options: {
    path: `${__dirname}/src/components/OgImage.tsx`,
  }
}

JSX構文で画像のテンプレートを作成

先ほど指定したpathのファイル内でOG画像のテンプレートをJSX構文で作成していく。このファイル内ではReactElementを返す関数をデフォルトexportする必要がある。

この関数の引数にはNodeを受け取ることができ、そのinternal.typeによって必要なOG画像を作成していく。デフォルトの設定ではMarkdownRemarkSiteの2種類のNodeを受け取ることができ、それぞれ記事用とサイト全体用のOG画像に利用する。

自分が作成した画像テンプレートは以下。作成する際はVercelのPlaygroudを使うと作成される画像をプレビューしながらテンプレートを作成することができて便利だった。

import { Node } from 'gatsby'

type Frontmatter = {
  title: string
  tags: string[]
}

export default function(node: Node) {
  if (node.internal.type === 'MarkdownRemark') {
    const frontmatter = node.frontmatter as Frontmatter
    const title = frontmatter.title
    const tags = frontmatter.tags

    return (
      <div
        style={{
          display: 'flex',
          padding: 48,
          height: '100%',
          backgroundColor: '#2e3440',
        }}
      >
        <div
          style={{
            height: '100%',
            width: '100%',
            display: 'flex',
            justifyContent: 'space-between',
            flexDirection: 'column',
            backgroundColor: 'white',
            color: '#000000d1',
            padding: 48,
            borderRadius: 12,
          }}
        >
          <div
            style={{
              display: 'flex',
              flexDirection: 'column',
            }}
          >
            <div style={{ fontSize: 64, maxWidth: 1000, fontWeight: 600 }}>{title}</div>
            <div style={{ display: 'flex', flexWrap: 'wrap', alignItems: 'center', marginTop: 16, gap: 16 }}>
              {tags.map((tag, i) => (
                <div
                  key={i}
                  style={{
                    fontSize: 32,
                    fontWeight: 400,
                    backgroundColor: 'rgb(229,231,235)',
                    padding: '8px 24px',
                    borderRadius: 200,
                  }}
                >
                  {tag}
                </div>
              ))}
            </div>
          </div>
          <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 16 }}>
            <div style={{ fontSize: 48, fontWeight: 400, display: 'flex', alignItems: 'center' }}>
              <img
                src="https://avatars.githubusercontent.com/u/44517313?v=4"
                width={72}
                height={72}
                style={{ borderRadius: '50%', marginRight: 16 }}
              />
              okaryo
            </div>
            
          </div>
        </div>
      </div>
    )
  } else {
    return (
      <div
        style={{
          display: 'flex',
          padding: 48,
          height: '100%',
          backgroundColor: '#2e3440',
        }}
      >
        <div
          style={{
            height: '100%',
            width: '100%',
            display: 'flex',
            justifyContent: 'center',
            alignItems: 'center',
            backgroundColor: 'white',
            color: '#000000d1',
            borderRadius: 12,
            fontSize: 144,
            fontWeight: 600,
          }}
        >
          <img
            src="https://avatars.githubusercontent.com/u/44517313?v=4"
            width={144}
            height={144}
            style={{ borderRadius: '50%', marginRight: 16 }}
          />
          okaryo.log
        </div>
      </div>
    )
  }
}

GraphQLからOG画像を取得

これでビルドして開発用のGraphQLページで確認してみると、以下のフィールドが追加されているはずだ。

  • allMarkdownRemarkOgImage
  • allSiteOgImage
  • markdownRemarkOgImage
  • siteOgImage

これを使ってOG画像を取得していくが、その前に準備が必要になる。記事と記事ごとのOG画像であるmarkdownRemarkOgImageを紐付けるには、記事のNodeIDが必要になる。GraphQLのクエリ中で記事のIDを取得できるようにするために記事ページを作成するcreatePagecontextに記事のNodeIDを追加しておく。

// gatsby-node.js
createPage({
  path: post.node.fields.slug,
  component: blogPost,
  context: {
    id: post.node.id, // これを追加
    slug: post.node.fields.slug,
    previous,
    next,
  },
})

こうすることでクエリ中で($id: String!)として記事のIDが取得できる。これとmarkdownRemarkOgImageの親NodeのIDを紐付けることで記事ごとのOG画像のパスを取得することができる。サイト全体のOG画像も以下のようなクエリで取得可能だ。

// query
`
query BlogPostQuery($id: String!) {
  markdownRemarkOgImage(parent: {id: {eq: $id}}) {
    attributes {
      publicURL
    }
  }
  siteOgImage {
    attributes {
      publicURL
    }
  }
}
`

// result
{
  "data": {
    "markdownRemarkOgImage": {
      "attributes": {
        "publicURL": "/static/8d5a6b2a951985acb20f041bf8f52e61/8d5a6b2a951985acb20f041bf8f52e61.png"
      }
    },
    "siteOgImage": {
      "attributes": {
        "publicURL": "/static/1d3db0d32c1e9ff61a30f15b2b9b6a2d/1d3db0d32c1e9ff61a30f15b2b9b6a2d.png"
      }
    }
  }
}

og:imageに画像を設定

最後に上記で取得したOG画像のパスをメタタグに設定すれば完了だ。

const yourSite = 'https://example.com'
const ogImagePath = data.markdownRemarkOgImage.attributes.publicURL

return (
  <>
    {/* other meta tags */}
    <meta property='og:image' content={yourSite + ogImagePath} />
    {/* other meta tags */}
  <>
)

おわり

簡単に記事ごとのOG画像を作成できたし、しかもそれをJSX構文で記述できるのでかなり体験が良かった。

気になった方は是非使ってみてほしい。気に入った方はぜひGitHubでスターをつけてほしい。

👉 https://github.com/okaryo/gatsby-plugin-satorare


関連記事
最新記事
プロモーション

This site uses Google Analytics.