ページの作成
親となるページを選択してください。
親ページに紐づくページを子ページといいます。
例: 親=スポーツ, 子1=サッカー, 子2=野球
子ページを親ページとして更に子ページを作成することも可能です。
例: 親=サッカー, 子=サッカーのルール
親ページはいつでも変更することが可能なのでとりあえず作ってみましょう!
この記事の要点
headを自分で書くのではなく、metadataオブジェクトかgenerateMetadataを export する。タグは Next.js が生成する。title.templateは子セグメントにだけ効く。同じ階層のpageには適用されないのでdefaultが必須。- メタデータは親から子へ浅くマージされる。子が
openGraphを書くと、親のopenGraphは丸ごと置き換わる。 favicon.ico、opengraph-image、robots.ts、sitemap.tsはファイルを置くだけで機能する。ImageResponseを使うと、JSX と CSS で OG 画像を動的に生成できる。ただし対応する CSS は限定的。
検索やソーシャル共有で正しく扱われるかどうかは、head 内のタグにかかっています。App Router では、これらを手書きするのではなく、ページやレイアウトから設定値をエクスポートする方式をとります。この記事では、その仕組みと SEO 上つまずきやすい点を扱います。
1静的なメタデータ
変化しない値は metadata オブジェクトをエクスポートします。layout と page のどちらからでも書けます。
// app/blog/layout.tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'ブログ',
description: '最新の投稿一覧です',
}
export default function Layout() {}
文字コードとビューポートの 2 つは、何も書かなくても常に出力されます。自分で meta charset を書く必要はありません。
なお、metadata と generateMetadata はサーバーコンポーネントでのみ使えます。ページを対話的にしたい場合は、page.tsx はサーバーのまま残し、対話部分を別ファイルのクライアントコンポーネントに切り出してください。また、同じセグメントから両方をエクスポートすることはできません。
2データに応じたメタデータ
記事タイトルのように内容が可変な場合は generateMetadata を使います。
// app/blog/[slug]/page.tsx
import { getPost } from '@/app/lib/data'
export async function generateMetadata({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
const post = await getPost(slug)
return {
title: post.title,
description: post.description,
}
}
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
const post = await getPost(slug)
return <div>{post.title}</div>
}
同じデータをメタデータと本文の両方で使うと、取得が 2 回走るように見えます。fetch は自動でメモ化されますが、DB 問い合わせなどは React.cache で包むと 1 回にまとめられます。上の例の getPost がそれです。
3タイトルのテンプレート
「ページ名 | サイト名」のような形をまとめて適用するには title.template を使います。
// app/layout.tsx
export const metadata: Metadata = {
title: {
template: '%s | Acme',
default: 'Acme', // template を使うときは必須
},
}
// app/about/page.tsx
export const metadata: Metadata = {
title: 'About',
}
// 出力: <title>About | Acme</title>
ここには重要な制約があります。template は子セグメントにだけ効き、同じ階層には効きません。そのため default が必須になっています。テンプレートを無視して単独のタイトルを出したい場合は title.absolute を使います。
ルートレイアウトで
template と default を定義し、各ページは title に短い名前だけを書く。page.tsx に template を書く。ページは末端なので子がなく、何の効果もない。4マージの規則に注意
メタデータはルートから順に評価され、同じキーは後勝ちで浅くマージされます。これが実務で最も事故を生む挙動です。
逆に、子が openGraph をまったく書かなければ、親のものがそのまま継承されます。
5正規 URL と metadataBase
OG 画像や正規 URL(canonical)は絶対 URL が必要です。毎回書くのは冗長なので、ルートレイアウトで metadataBase を設定します。
// app/layout.tsx
export const metadata: Metadata = {
metadataBase: new URL('https://example.com'),
alternates: {
canonical: '/',
},
openGraph: {
images: '/og-image.png',
},
}
これで相対パスが自動的に絶対 URL に展開されます。設定せずに相対パスを書くとビルドエラーになるので、早い段階で入れておくのが安全です。
検索エンジンへの指示は robots で書けます。特定のページだけインデックスさせたくない場合に使います。
export const metadata: Metadata = {
robots: {
index: false,
follow: true,
},
}
6ファイルを置くだけで効くもの
| ファイル | 役割 |
|---|---|
app/favicon.ico | ブラウザのタブや検索結果に出るアイコン |
app/icon.png / app/apple-icon.png | 各種プラットフォーム向けアイコン |
app/opengraph-image.png | SNS 共有時に出る画像 |
app/robots.ts | robots.txt を生成する |
app/sitemap.ts | sitemap.xml を生成する |
これらは階層ごとに置けます。app/blog/opengraph-image.png を置けば、ブログ配下だけ別の画像になります。より深い場所のファイルが優先されます。
ファイルベースの指定は、metadata オブジェクトでの指定より優先されます。両方書いている場合はファイル側が勝ちます。
7OG 画像を動的に作る
記事タイトル入りの OG 画像を自動生成したい場合は、opengraph-image.tsx を置いて ImageResponse を使います。
// app/blog/[slug]/opengraph-image.tsx
import { ImageResponse } from 'next/og'
import { getPost } from '@/app/lib/data'
export const size = { width: 1200, height: 630 }
export const contentType = 'image/png'
export default async function Image({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
const post = await getPost(slug)
return new ImageResponse(
(
<div
style={{
fontSize: 64,
background: 'white',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
{post.title}
</div>
)
)
}
ここで使える CSS は限定的で、flexbox と一部のプロパティのみです。display: grid は動きません。凝ったレイアウトを組もうとすると空振りするので、シンプルな構成にとどめるのが無難です。
バージョン 16 では、この画像生成関数が受け取る params も Promise になっています。await を忘れないでください。
8メタデータのストリーミング
事前生成できないページでは、Next.js は本文を先に送り、generateMetadata が解決した時点でタグを追加します。これにより初期表示が速くなります。
JavaScript を実行して DOM 全体を見るクローラー(Googlebot など)はこの形でも正しく解釈します。一方、JavaScript を実行しない種類のボットに対しては、従来どおり描画をブロックして head 内にタグを出す挙動が維持されます。どのユーザーエージェントをその扱いにするかは htmlLimitedBots で調整できますが、既定のままで問題ないケースがほとんどです。
9つまずきやすいところ
- クライアントコンポーネントで
metadataを export した:無視されます。サーバーコンポーネント側へ移します。 templateが効かない:同じ階層には効きません。親のレイアウトに書きます。- OG の description が消える:子で
openGraphを定義すると親のものは丸ごと置き換わります。共通部分を変数化して展開してください。 - 相対パスでビルドエラー:
metadataBaseを設定していません。 themeColorが効かない:バージョン 14 でmetadataから分離され、viewport側の設定に移りました。
検索とシェアの準備ができたら、次は表示速度に直結する画像とフォントに進みます。
ページの作成
親となるページを選択してください。
親ページに紐づくページを子ページといいます。
例: 親=スポーツ, 子1=サッカー, 子2=野球
子ページを親ページとして更に子ページを作成することも可能です。
例: 親=サッカー, 子=サッカーのルール
親ページはいつでも変更することが可能なのでとりあえず作ってみましょう!
子ページはありません
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 Spring Frameworkのアノテーション一覧
- 7 3Dグラフィックスとは|モデリング/レンダリング/主要ソフトウェア (Blender / Maya)
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- PHPでのファイルアップロード方法|multipart/form-dataと$_FILES 2026-08-07 17:15:57
- PHP $_SERVERとは|DOCUMENT_ROOT・PHP_SELFなど主な要素 2026-08-07 17:15:57
- Scratchのアカウントの作り方|メールアドレスとユーザー名の登録手順 2026-08-07 17:15:57
- djangoにおけるMVCアプリケーション実装例 2026-08-07 17:15:57
- Bluetoothとは|仕組み・BLEとClassicの違い・用途 2026-08-07 17:15:57
- Spring Bootのテスト入門|@SpringBootTestとJUnitでの単体・結合テスト 2026-08-07 17:15:57
- Java とは?言語仕様・JVM・主要フレームワーク一覧 2026-08-07 17:15:56
- インストール方法(Windows版) 2026-08-07 17:15:56
- APIキー取得方法 2026-08-07 17:15:56
- 【PHPフレームワーク】Laravelの使い方 2026-08-07 17:15:56
- Scratch 作品の公開方法 2026-08-07 17:15:56
- Google OAuth 2.0 認証の実装方法 2026-08-07 17:15:56
- データベースとは?RDB / NoSQL / SQL の基礎 2026-08-07 17:15:56
- インストール(eclipseプラグイン) 2026-08-07 17:15:56
- Spring Frameworkの使い方 2026-08-07 17:15:56
コメントを削除してもよろしいでしょうか?