◼️

Next.jsのApp Routerで404ページのtitleを設定する

2024/02/15に公開

Next.jsのApp Routerで404ページのtitleを設定する方法がすこしややこしいので解説します。

普通のページでtitleを設定

404ページでのtitle設定の前に通常のページでtitle設定を行う方法を解説します。

page.tsx内で静的なtitleならmetadataオブジェクト、動的なtitleならgenerateMetadata関数をexportすることで設定ができます。

metadataオブジェクトではtitleプロパティにtitleを指定してexportします。

app/page.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: 'サイトのタイトル'
}

generateMetadata関数では関数内で動的にtitleを生成するコードを記述して生成したtitleをプロパティにを指定してexportします。

app/page.tsx
import type { Metadata } from 'next'
 
export function generateMetadata(): Metadata {
  // タイトルを動的に取得するコード
  const { title } = getPageTitle()
  return {
    title
  }
}

https://nextjs.org/docs/app/api-reference/functions/generate-metadata

title templateの利用

よく利用する「ページタイトル | サイト名」などの指定はtitle templateを利用すると簡単に設定可能です。

layout.tsxなどで以下のようにtemplateプロパティを指定しておくと下層のpage.tsxで指定されたtitleにテンプレートを適用して反映してくれます。

app/layout.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: {
    template: '%s | サイト名',
    default: 'サイト名'
  }
}

同階層のpage.tsxmetadataの指定がない下層のpage.tsxではdefaultプロパティの指定が採用されます。

404ページでtitleを設定

Next.jsのApp Routerではnot-found.tsxというファイルを作成することで404ページに適用することができます。

以下のようにmetadataオブジェクトをexportしておくことでtitleには「404 | サイト名」という値が反映されます。

app/not-found.tsx
import type { Metadata } from 'next'

export const metadata:Metadata = {
  title: '404'
}

export default function Page() {
  return (
    <div>
      <h1>404ページ</h1>
    </div>
  );
}

https://nextjs.org/docs/app/api-reference/file-conventions/not-found

動的ルーティングでtitleを設定

動的ルーティングでtitleを設定する場合でも普通のページでのtitle設定と変わりません。
動的ルーティングの場合は動的にtitleをつけることが多いのでgenerateMetadata関数を使うことが多いでしょう。

以下のように http://localhost:3000/xxxでページが表示でき、xxxの部分をtitleに含めようとすると以下のような指定になります。

app/[slug]/page.tsx
import type { Metadata } from 'next'

type Props = {
  params: {
    slug: string
  }
}

export function generateMetadata({ params }:Props): Metadata {
  return {
    title:`${params.slug}のページ`
  }
}

export default function Page({ params }:Props) {
  return (
    <h1>{params.slug}のページ</h1>
  );
}

この場合、http://localhost:3000/aaにアクセスした場合のtitleは「aaのページ | サイト名」に、http://localhost:3000/bbにアクセスした場合のtitleは「bbのページ | サイト名」になります。

動的ルーティングで404ページにする

動的ルーティングで404ページを指定するにはnotFound関数を利用します。

今回はhttp://localhost:3000/aahttp://localhost:3000/bbしか許可せずにそれ以外を404にするとしましょう。

その場合以下のようにslugがaaとbb以外の場合にはnotFound関数を実行します。

app/[slug]/page.tsx
+ import { notFound } from 'next/navigation'
import type { Metadata } from 'next'

type Props = {
  params: {
    slug: string
  }
}

export function generateMetadata({ params }:Props): Metadata {
  return {
    title:`${params.slug}のページ`
  }
}

export default function Page({ params }:Props) {

+  if(!['aa','bb'].includes(params.slug)){
+    notFound()
+  }

  return (
    <h1>{params.slug}のページ</h1>
  );
}

これで、http://localhost:3000/ccにアクセスした場合404ページが表示できるのが確認できるのですがtitleが「ccのページ | サイト名」になっており、app/not-found.tsxで指定したtitleの指定は反映されていません。

動的ルーティングで404ページでのtitle指定

app/not-found.tsxで指定したtitleの指定を反映したい場合はgenerateMetadata関数内でもnotFound関数を実行する必要があります。

app/[slug]/page.tsx
import { notFound } from 'next/navigation'
import type { Metadata } from 'next'

type Props = {
  params: {
    slug: string
  }
}

export function generateMetadata({ params }:Props): Metadata {

+  if(!['aa','bb'].includes(params.slug)){
+    notFound()
+  }

  return {
    title:`${params.slug}のページ`
  }
}

export default function Page({ params }:Props) {

  if(!['aa','bb'].includes(params.slug)){
    notFound()
  }

  return (
    <h1>{params.slug}のページ</h1>
  );
}

これでは複雑な条件の場合に、ページコンポーネント関数内でもgenerateMetadata関数内でも重複するエラーハンドリングが発生してちょっと面倒です。

app/not-found.tsxで共通のtitleを設定

上記のように全ページのgenerateMetadata関数内でもエラーハンドリングを行うのは現実的ではありません。

これは、app/not-found.tsxを以下のように変更することで全ページで共通のtitleを設定することが可能になります。

app/not-found.tsx
-import type { Metadata } from 'next'
- 
-export const metadata:Metadata = {
-   title: '404'
-}
- 
export default function Page() {
  return (
    <div>
+     <title>404 | サイト名</title>
      <h1>404ページ</h1>
    </div>
  );
}

JSX内にtitle要素を指定することでページタイトルに反映されるようになります。
ただ、title templateの指定は有効にならないのでtemplateの内容を継承する形で指定する必要があります。

ページ事にnotFoundを指定するよりかは格段と楽になるので試してみてください。

株式会社トゥーアール

Discussion