2022-08-08

gatsby-plugin-react-helmetが廃止予定に!ヘッダはAPIを使おう【Gatsby.js】

Gatsby.jsにてtitleやmetaタグといったヘッダ要素を書くためのAPIが公式で導入されreact-helmetを使う必要がなくなった。性能的にもこちらが良いとのことで使用法を解説。

Article Image
使用ソフトウェアバージョン備考
Gatsby.js4.20APIは4.19から使用可能

gatsby-plugin-react-helmetが廃止予定!

Gatsby.js v4.19よりgatsby-plugin-react-helmetが将来的に廃止される模様。

代わりに、公式でGatsby Head APIと呼ぶ機能が実装されたため、プラグインなしでheadタグ内にtitlemetaといったタグを追加することが出来るようになっている。

gatsby-plugin-react-helmetのページに廃止予定(will be deprecated)と書かれているため、いずれ更新がとまると思われる。

gatsby-plugin-react-helmet | Gatsby

APIを使うメリットは?

いままでreact-helmetに依存することでhead内にタグを書き込んでいた人が多いと思う。

今回こちらを廃止して切り替えということは、わざわざAPIに書き換える作業コストが必要となる。

それに見合うメリットはあるのだろうか。

公式によると

  1. パフォーマンスの向上
  2. 実装が簡単
  3. データ容量は変わらない
  4. 今後新しいReact機能にも対応

というメリットがあるらしい。

特に最近はWebサイトの性能評価がどんどん厳しくなっているため(1)のパフォーマンス向上で有利かと個人的に考えている。

もちろん現状のgatsby-plugin-react-helmetはまだ正常動作するため、すぐさま切り替える必要はない。

注意1:プラグインのアンインストール必須

実装前にgatsby-config.jsからgatsby-plugin-react-helmetをコメントアウト(可能ならアンインストール)する。

これを行わずAPIで実装するとエラーでビルドできない。

アンインストールのコマンド例を掲載しておく

npm uninstall gatsby-plugin-react-helmet react-helmet

念のため上はアンインストールコマンドなので慎重に

注意2:コンポーネントには実装できない

Gatsby Head APIを使った新しい方法はコンポーネントには実装できない。

pagestemplateにだけ利用できる。

どういうこと?

たとえばindexページはpagesに当たる(固定ページ)

その中からLayoutなどのコンポーネントを呼び出して使用するというのは基本である。

このときLayout内からheadタグに書き込むことはできないという意味。

この場合親になるindexからヘッダを書き込む。

つまりsrc > componentsに入っているjsからは使えないと覚えておいて良いだろう。

TemplateもOK

他にもgatsby-node.jsからcreatePages関数で生成されるページのテンプレートからAPIを呼ぶことが出来る。

したがってBLOG記事などビルド時に生成されるページでもこのAPIは使用できる。

基本の実装

Gatsby Head APIを使うにあたって特別なインポートは必要ない。

Headという関数をexportしてやりその中にtitlemetaタグを書いておくだけで自動的に挿入される。

ただしGatsby.jsのバージョンが4.19以上である必要がある。

次が最も簡単な例。

import * as React from "react"

const headTest = () => {
    return (<div>test</div>)
}

export default headTest

//次が新しいコード。必ずHeadという名前の関数。
export const Head = () => {
    return (<title>ヘッダテスト</title>)
}

このようにHeadという関数を別途exportしており、その中のreturnを使ってheadに記述したいタグを書いている。

少々理解しにくい人もいるかもしれないが、このHead()関数内でreturnしたタグがGatsby側で処理されてheadに注入されるという仕組みである。

言うまでもないかもしれないが通常headタグに書かないようなタグは使えない。

使えるのは次のタグ。

link, meta, style, title, base, script, noscript.

記事のタイトルなどからタグを変化させたい

このままだとハードコードしたデータしか使えない気がするが、そんなことはない。

記事名からタイトルを変えたい、説明文を変えたいなどのケース。

Headを次のように書いて外部からのプロパティを読み込める。

export const Head = ({ location, params, data, pageContext }) => (
  <>

各プロパティを説明すると

  • location: location.pathnameとしてやることで現在のURLが取得できる
  • params: URLのパラメタを受け取る。matchPathという機能を使うときだそうだが使ったこと無いので不明
  • data: GraphQLで記述したデータが入ってくる。つまりBLOG記事の内容やタイトル、日付、画像のアドレスなどはここから取る。
  • pageContext: createPage関数で渡ってくるデータを受け取る。

一番よくあるのはdataを使うことだと思う。

これはどこからくるのかというと、各ページにて

export const query = graphql`クエリ~~

のように指定して取ってくるクエリのことを指している。

これを利用することでtemplateで様々なページが生成されたときにも個別のタイトルなどを付与することが可能。

スタティッククエリで

またはuseStaticQueryを利用しても良さそうだ。

import * as React from "react"
import { graphql, useStaticQuery } from "gatsby"

const headTest = () => {
    return (<div>test</div>)
}

export default headTest

export const Head = () => {
    const metaData = useStaticQuery(graphql`
        query {
            site {
              siteMetadata {
                title
                description
              }
            }
          }
      `)
    return (
        <>
            <title>{metaData.site.siteMetadata.title}</title>
        </>
    )
}

こちらは最小限しかテストしてないので動かないクエリもあるかもしれない。

SEO用のコンポーネントとして読み込みたい

上の方法だけだとページごとに別々のHead()関数として書いてやらないといけないような気がする。

実はコンポーネントの中にHead()が使えないだけでHead()中身をコンポーネント化することは可能。

例えば次のようなページを書いて

import * as React from "react"
import Seo from '../components/seo-head' //ここでhead用コンポーネントを呼ぶ

const headTest = () => {
    return (<div>test</div>)
}

export default headTest

//Head()の中でコンポーネントを利用
export const Head = () => {
    return (
        <Seo
            title="ここにタイトル"
            twitter="Ultra-Noob"
        />
    )
}

そしてSEOコンポーネント(ここではseo-head.js)の中身は

import React from "react"

const SEO = ({ title, twitter }) => {

    return (
        <>
            <title>{title}</title>
            <meta name="twitter:creator" content={twitter} />
        </>
    )
}

export default SEO

ブラウザで確認すると次のタグが正しく入っている。

実行結果

このようにHead()関数内はコンポーネント化できるので再利用が可能。

各ページごとにHead()関数は毎回exportしてやる必要があるので注意。

その中身をコンポーネント化できますよという意味。

JSXで条件分岐

上で特定のタグしか使えないと書いたが、もちろんJSXなので条件分岐が可能。

私が使うケースだとnoindexmetaタグを入れるかどうかを分ける場合。

上のSEOコンポーネントに書き加えて

const SEO = ({ title, twitter, isNoindex }) => {
    return (
        <>
            <title>{title}</title>
            <meta name="twitter:creator" content={twitter} />
            {
                isNoindex ? <meta name="robots" content="noindex" /> : null
            }
        </>
    )
}

isNoindextrueを入れたときだけnoindexのメタタグが書かれるようになる。

おまけ:idで分ける方法も?

公式よりこの他にもidを使うことでタグを分けられるかもしれない。

公式のDeduplicationの項

これに関しては同じタグを排除する機能なので違うかも?

テストしていないので公式を読んだ上で試してもらえたらと思う。

パフォーマンスは向上したのか?

謎の技術研究部の本番サイトで実装前と実装後のLighthouseのスコアを計測した。

とは言ったものの殆ど差がでなかったのでスコアも掲載しない。

headタグの仕組みを改善した程度で大きくスコアが上がるということが無いというのは少し考えれば当たり前ではある。

とは言え、過去の私のテストから外部プラグインのインポートは少ないほうがスコアが上がるのでreact-helmetを読み込まないだけ有利になっていると予想できる。

少しでも効果があるのならば実装するに越したことはない。

より詳しく知りたい

公式のAPIページのみならず次のSEOコンポーネント解説ページが参考になる。

Adding an SEO Component | Gatsby(英文)

この記事でも多くの箇所で上を参照させていただいている。

より深く知りたい場合は読んでもらえたらと思う。

さいごに

相変わらずGatsby.jsの記事に関しては解説が難しく理解してもらえてるかどうか怪しい。

ひとまず実装されたばかりで日本語の記事が見当たらなかったため率先して執筆させていただいた。

パフォーマンスと今後のアップデートを考慮して当サイトでは早速実装した。



この記事のタグ

この記事をシェア


謎の技術研究部 (謎技研)