Next.jsでどうしてもReact Helmetを使いたい場合の覚書

Next.jsでOGPなどのmetaタグを取り扱う場合、next/headを使うのが基本です。が、別のアプリからマイグレした場合などで、どうしてもReact helmetからの移行が難しいケースもあります。

そんな場合の対処法について、Next.jsのExampleをベースに、自分向けにまとめました。

React HelmetはNext.jsでそのまま動かない

原因を深掘りしていないので不明ですが、React Helemetで書いたmetaタグがNext.jsのSSRでレンダリングされていない現象に遭遇しました。

基本的にnext/headでReact Helmetができることはカバーできるので、そっちに切り替えるのが正攻法ではあると思います。が、Exampleでwith-react-helmetがあったことと、対応にあまり時間をかけたくなったことの2点からそのままHelmetを使うことに決定。

_document.tsxreact-helmetのSSR処理を実行

ExampleのコードをTypeScriptで動くようにしたものが以下です。anyで乗り切っていますが、将来的にnext/headに変える前提のため省力化したためです。

import React from "react";
import { Helmet } from "react-helmet";
import Document, { Html, Head, Main, NextScript } from "next/document";

export default class MyDocument extends Document<{
  helmet: any;
}> {
  static async getInitialProps(args: any) {
    const documentProps = await super.getInitialProps(args);
    return { ...documentProps, helmet: Helmet.renderStatic() };
  }
  // should render on <html>
  get helmetHtmlAttrComponents() {
    return this.props.helmet.htmlAttributes.toComponent();
  }

  // should render on <body>
  get helmetBodyAttrComponents() {
    return this.props.helmet.bodyAttributes.toComponent();
  }

  // should render on <head>
  get helmetHeadComponents() {
    return Object.keys(this.props.helmet)
      .filter((el) => el !== "htmlAttributes" && el !== "bodyAttributes")
      .map((el) => this.props.helmet[el].toComponent());
  }

  render() {
    return (
      <Html {...this.helmetHtmlAttrComponents}>
        <Head>{this.helmetHeadComponents}</Head>
        <body {...this.helmetBodyAttrComponents}>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

参考

Comment