Next.jsでhtmlタグのprefix属性をページごとに出し分ける
Custom DocumentでNext.jsで各ページのHTMLタグを上書きする
Next.js には Custom Document という機能があります。これは、各ページで共通の HTML を上書きするための機能です。
html タグ、body タグに属性を付与したり、SEO 対策をするために meta タグを記述することに使われます。
今回は、OGP の公式ドキュメントに記載されている prefix 属性と値(prefix="og: http://ogp.me/ns#"
)を html タグに付与する方法を紹介します。
Next.jsでhtmlタグにprefixを付与する
pages
ディレクトリに _document.tsx
を作成します。以下のように記述すると、全てのページの html タグに prefix 属性が付与されます。
import Document, { Head, Html, Main, NextScript } from 'next/document'
class MyDocument extends Document {
render() {
return (
<Html prefix="og: http://ogp.me/ns#">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
404ページはprefixを付与しないための設定をする
Next.js では pages
ディレクトリに 404.tsx
を作成すると、リソースが存在しないときに自動で 404 ページが表示されます。
この 404 ページなど、特定のページでは html タグから prefix 属性を削除したいケースがあると思います。
その時は、getInitialProps
内でパスの判定をして場合分けをしましょう。
import Document, { Html, Head, Main, NextScript } from 'next/document'
const htmlPrefix = 'og: http://ogp.me/ns#'
class MyDocument extends Document<{ prefix: string | null }> {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
// 404 ページには prefix を設定しない
const prefix = ctx.pathname.startsWith('/404') ? null : htmlPrefix
return { ...initialProps, prefix }
}
render() {
const prefix = this.props.prefix
return (
<Html prefix={prefix}>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
getInitialProps
の return 前にconsole.log(ctx.pathname, prefix)
を差し込み、$ npm run build
を実行してみます。すると、以下のようなログが出力されます。
$ npm run build
> [email protected] build /sample_program/react/nextjs-playground
> next build
info - Creating an optimized production build
info - Compiled successfully
info - Collecting page data
/ og: http://ogp.me/ns#
/404
/recoil og: http://ogp.me/ns#
/redux og: http://ogp.me/ns#
info - Generating static pages (32/32)
info - Finalizing page optimization
Page Size First Load JS
┌ ○ / 2.9 kB 81.9 kB
├ /_app 0 B 79 kB
├ ○ /404 279 B 79.3 kB
├ ○ /recoil 15.5 kB 94.6 kB
├ ○ /redux 626 B 79.7 kB
+ First Load JS shared by all 79 kB
├ chunks/1e4bfcbb482c0ad992d70a6f3b42612b47faafd5.172fb8.js 5.19 kB
├ chunks/e2988e983c224d75e4d495ad6342b845f7d73227.4953e9.js 13.8 kB
├ chunks/framework.4d50c5.js 42.1 kB
├ chunks/main.5f9b00.js 6.77 kB
├ chunks/pages/_app.858252.js 10.4 kB
├ chunks/webpack.50bee0.js 751 B
└ css/fda66c1c8420c26251ed.css 3.16 kB
λ (Server) server-side renders at runtime (uses getInitialProps or getServerSideProps)
○ (Static) automatically rendered as static HTML (uses no initial props)
● (SSG) automatically generated as static HTML + JSON (uses getStaticProps)
(ISR) incremental static regeneration (uses revalidate in getStaticProps)
ログを見ると、/404
では空文字であることがわかりますね。
next export で出力を確認する
$ next build && next export
を実行し、 HTML を出力して結果を確認してみましょう。
index.html
と404.html
のを比較します。
<!DOCTYPE html>
<html prefix="og: http://ogp.me/ns#">
<head>
<!-- ... -->
<!DOCTYPE html>
<html>
<head>
<!-- ... -->
これで prefix 属性の出し分けができました!
ページ遷移時にhtmlの変更に対処する
なお、上記のままでは 404 ページから通常のページに遷移したとき、prefix 属性が存在しないままになります。反対に、通常ページから 404 ページにリンクしている場合、404 ページでも prefix 属性が付与されたままになります。
これは、そもそもクライアントサイドルーティングでは Next.js が ページごとに JS で表示するコンテンツを書き換えているのですが、その際 html タグまでは書き換えないからです。
OGP は特定の Web ページを SNS 等でリッチに表示するための手段なので、ページ遷移時の挙動まで考慮しなくても良いかもしれません。
ただ、もしページ遷移時も prefix を出し分けしたいのであれば、以下のような Custom Hooks を作って対応可能です。
htmlにprefix属性を付与するCustom Hooks
prefix 属性を付与したり削除するためには、タグの属性を操作するsetAttribute
、removeAttribute
、hasAttribute
という API を活用します。
まず、prefix 属性を付与するuseAddHtmlPrefix
を作成します。
import { useRouter } from 'next/router'
import { useEffect } from 'react'
const prefix = 'prefix'
const htmlPrefix = 'og: http://ogp.me/ns#'
export default function useAddHtmlPrefix() {
const { pathname } = useRouter()
const is404Page = pathname.startsWith('/404')
useEffect(() => {
if (is404Page) {
return
}
const hasHtmlPrefix = document.documentElement.hasAttribute(prefix)
// prefix が存在しない場合、 404 以外のページに prefix を設定する
if (!hasHtmlPrefix) {
document.documentElement.setAttribute(prefix, htmlPrefix)
}
}, [is404Page])
}
この Custom Hooks を_app.tsx
で呼び出します。
import { AppProps } from 'next/app'
import useAddHtmlPrefix from '~/hooks/useAddHtmlPrefix'
const App = ({ Component, pageProps }: AppProps) => {
useAddHtmlPrefix()
return <Component {...pageProps} />
}
export default App
htmlのprefix属性を削除するCustom Hooks
import { useEffect } from 'react'
const prefix = 'prefix'
export default function useRemoveHtmlPrefix() {
useEffect(() => {
const hasHtmlPrefix = document.documentElement.hasAttribute(prefix)
if (hasHtmlPrefix) {
document.documentElement.removeAttribute(prefix)
}
}, [])
}
この Custom Hooks を404.tsx
で呼び出すと、html から prefix 属性を削除できます。
import useRemoveHtmlPrefix from '~/hooks/useRemoveHtmlPrefix'
export default function Error() {
useRemoveHtmlPrefix()
return <div>404</div>
}
以上、Next.js で html の prefix 属性の値prefix="og: http://ogp.me/ns#"
をページごとに出し分ける方法でした。
参考
Happy Coding 🎉