stin's Blog

ブログに next-seo を導入した


適当にやってた meta タグ設定をちゃんとしました

Next.js のプロジェクトの meta タグ類を設定してくれるライブラリ next-seo をインストールしました。

今まで適当に meta タグをセットしていて、特に OGP 系は必須プロパティとかも入れずにやっていましたが、今回 next-seo を導入するにあたってちゃんと調べました。

ちなみに next-seo という名前ですが、 Search Engine Optimization は meta タグ設定したくらいでは良くなるとは思っていないので、どちらかと言うと Twitter 等でリンクシェアしてもらった時にきれいなリンクカードが展開されてほしいという考えから入れました。

Open Graph Protocol

OGP(Open Graph Protocol) は Facebook が策定した meta タグの埋め込み方のルールです。ルールに沿って HTML に meta タグを埋め込むことで、他のサイトや Web サービスが同じ meta データを取得することができ、リッチなリンクカードの表示にデータを使用してもらえたりします。このブログのリンクがカード形式になっているのも、OGP のおかげです。

OGP には必須のプロパティがあります。(以前はこれを知らずに含めていないプロパティがありました…)

  • og:title
  • og:type
  • og:image
  • og:url

さらに og:type の値によっては追加で必須になるプロパティも存在します。詳しくは公式ドキュメントを御覧ください。

このサイトはブログサイトなので、 og:type"article""website" だけを指定できれば十分です。プロフィールページを作ったら profile が指定できるようにまた改修します。

SEO コンポーネント

next-seo から export されている NextSeo コンポーネントは受け取る props が多く、そのまま各ページで使うには記述が煩雑になりそうでした。

なので NextSeo をラップした独自の SEO コンポーネントを宣言して、各ページで使用します。 SEO コンポーネントは NextSeo に比べて props の数を大幅に絞っており、コンポーネントとして扱いやすくなっているのではないでしょうか。

型定義は以下のようにしました。

type BaseProps = {
  pagePath: string;
  title?: string;
  description?: string;
  noindex?: boolean;
};

type ForWebsiteProps = {
  type: "website";
  publishedTime?: undefined;
  modifiedTime?: undefined;
  tags?: undefined;
};

type ForArticleProps = {
  type: "article";
  publishedTime: string;
  modifiedTime: string | undefined;
  tags: string[];
};

export const SEO: React.VFC<BaseProps & (ForWebsiteProps | ForArticleProps)> = {...};

BaseProps は全画面で必須のプロパティです。 ForWebsitePropsForArticleProps<meta property="og:type"> の値によって必須にするかしないかが決まるプロパティになります。

前節で触れたとおり、 og:type によって指定できるプロパティが変化します。それを TypeScript の Discriminated Union で条件付きの必須を表現しました。

実装はこんな感じです。

export const SEO: React.VFC<BaseProps & (ForWebsiteProps | ForArticleProps)> = ({
  type,
  pagePath,
  title,
  description,
  noindex,
  publishedTime,
  modifiedTime,
  tags,
}) => {
  const siteTitle = config.siteTitle;

  return (
    <NextSeo
      title={title}
      titleTemplate={`%s | ${siteTitle}`}
      defaultTitle={siteTitle}
      noindex={noindex}
      twitter={{
        cardType: "summary_large_image",
        handle: `@${config.social.twitter}`,
      }}
      openGraph={{
        type: type,
        url: config.siteUrl + pagePath,
        title: title,
        description: description,
        site_name: siteTitle,
        images: [
          {
            url: config.siteUrl + staticPath.images.ogimage_png,
            width: 1200,
            height: 630,
          },
        ],
        article:
          type === "article"
            ? {
                authors: [`https://twitter.com/${config.social.twitter}`],
                publishedTime: publishedTime,
                modifiedTime: modifiedTime,
                tags: tags,
              }
            : undefined,
      }}
    />
  );
};

config オブジェクトは SNS のユーザー名やこのサイトがデプロイされている URL の文字列を持っている定数です。どう考えても環境変数 proccess.env に持たせたほうがいいと思います。

propsNextSeo に渡すだけで特殊な処理はしません。 titleopenGraph.title など重複する項目がいくつかあるので、同じ props が渡るようにしています。

他に NextSeoprops として指定できるものは公式の README を参考にしてください。

まとめ

このブログサイトに next-seo を導入しつつ、 OGP をちゃんと調べたよというお話でした。

必須プロパティくらいはちゃんと埋めような!