Next.jsとは?Reactとの違いとPages Routerの基本を解説
Next.jsの役割をPages Routerのコードで解説。SSG・SSR・ハイドレーションの違い、静的出力の制約、SEOや安全性を考える際の注意点を学べます。
Next.jsは、Reactで作る画面に、URLに応じたページの表示、HTMLの事前生成、データ取得、ビルドなどの仕組みを加えるフレームワークです。ReactはUIを構築するライブラリで、Next.jsはアプリケーション全体を組み立てるための機能を提供します。
この記事では、Pages Routerを前提に、SSG・SSR・ブラウザでの処理の違いを説明します。コードはTypeScriptを使用し、src/pages/があるプロジェクトで試せます。App Routerではページの配置やデータ取得のAPIが異なるため、同じコードをそのまま移すことはできません。
Next.jsでできること
Next.jsには、ファイルの配置からURLを決めるルーティング、Reactの画面の事前レンダリング、ページ間の移動、CSS Modules、TypeScriptの支援などがあります。サーバーを動かす構成では、API RoutesでHTTPリクエストを処理することもできます。
Pages Routerでは、src/pages/index.tsxがトップページ、src/pages/about.tsxが/aboutに対応します。再利用する部品はsrc/components/などに置いて、ページから読み込みます。この部品用ディレクトリの名前は自由です。
次のコードをsrc/pages/about.tsxに保存すると、紹介ページとトップページへ戻るリンクを作れます。
import Link from 'next/link';
export default function AboutPage() {
return (
<main>
<h1>SEの学習ノート</h1>
<p>TypeScriptとNext.jsの実践例をまとめています。</p>
<Link href="/">トップへ戻る</Link>
</main>
);
}
Linkはページ間の移動に使うNext.jsのコンポーネントです。Next.js 13以降の通常の書き方では、子要素にさらにaタグを追加する必要はありません。
HTMLの生成方式とブラウザの役割
SSGはビルド時にHTMLを用意する
SSGはStatic Site Generation、つまり静的サイト生成の略です。Next.jsでは、ビルド時にページのHTMLを作っておく方法を指します。Static Site Generatorは生成ツールを指す言葉で、生成方式そのものとは区別します。
公開記事、製品の説明、会社案内など、あらかじめ内容を取得でき、多くの利用者に同じ情報を配信するページに向いています。リクエストのたびに記事を取得する必要がなく、生成物をキャッシュしやすい点が利点です。
src/pages/build-info.tsxに、生成した日時を表示するページを作ってみます。
import type { GetStaticProps } from 'next';
type Props = {
generatedAt: string;
};
export const getStaticProps: GetStaticProps<Props> = async () => {
return {
props: { generatedAt: new Date().toISOString() },
};
};
export default function BuildInfoPage({ generatedAt }: Props) {
return (
<main>
<h1>ビルド情報</h1>
<p>生成日時: <time dateTime={generatedAt}>{generatedAt}</time></p>
</main>
);
}
通常のビルドでは、getStaticPropsがビルド環境で実行され、返した値を使ってHTMLが生成されます。開発サーバーでは確認のためリクエスト時に再実行されるので、本番と同じタイミングだと思わないようにします。ISRを使うサーバー構成では、公開後の再生成も可能です。
SSGでもJavaScriptによるフォーム操作やブラウザからのデータ取得はできます。ただし、HTMLに埋め込まれた記事本文を更新するには、静的出力の場合は再ビルドと再配置が必要です。大量のページや遅い外部APIはビルド時間に影響します。
SSRはリクエスト時にHTMLを生成する
SSRはServer-side Renderingの略です。Pages RouterではgetServerSidePropsを使い、リクエストに応じたデータからHTMLを作れます。ログイン状態などのリクエスト情報が必要なページや、その時点の情報を返したいページで検討します。
SSGの例とは別に、src/pages/request-info.tsxへ次のコードを保存すると、サーバーで処理した日時を表示できます。このページはNext.jsサーバーが必要で、静的出力用のoutput: 'export'とは併用できません。
import type { GetServerSideProps } from 'next';
type Props = {
requestedAt: string;
};
export const getServerSideProps: GetServerSideProps<Props> = async ({ res }) => {
res.setHeader('Cache-Control', 'private, no-store');
return {
props: { requestedAt: new Date().toISOString() },
};
};
export default function RequestInfoPage({ requestedAt }: Props) {
return (
<main>
<h1>リクエスト情報</h1>
<p>処理日時: <time dateTime={requestedAt}>{requestedAt}</time></p>
</main>
);
}
SSRでは、外部APIの応答待ちやサーバー処理の時間もレスポンスに含まれます。導入すれば必ずSSGやブラウザ描画より速くなるわけではありません。取得するデータ量、キャッシュ、配信環境を含めて測定します。
ブラウザで動く処理との関係
サーバーやビルド環境が作ったHTMLを受け取った後、Reactがブラウザ上でイベント処理などを結び付けます。この処理をハイドレーションと呼びます。SSGやSSRのページでも、ボタンの操作や入力欄の更新にはブラウザ側のJavaScriptが必要です。
window、document、localStorageなどはブラウザのAPIです。ページを描画する関数の中で無条件に読み取ると、サーバー側のレンダリングでエラーになることがあります。ブラウザとの同期処理にはuseEffectを使い、SSRと最初のブラウザ描画が一致するように初期表示を設計します。
静的ファイルとして公開する場合
HTML・CSS・JavaScriptだけをWebサーバーへ置きたい場合は、next.config.jsで静的出力を有効にします。次はCommonJS形式の設定例です。
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export',
trailingSlash: true,
};
module.exports = nextConfig;
この設定でnpm run buildを実行すると、out/へ配信用ファイルが出力されます。先ほどのSSRページはこの構成に含められません。Pages RouterのAPI Routes、ISR、デフォルトの画像最適化サーバーなども静的出力では利用できません。必要なら外部APIや画像配信サービスを組み合わせます。
SEOや安全性について誤解しやすい点
SSGとSSRは、初期HTMLに主要な文章を含める手段です。検索順位やAI検索への掲載を保証する機能ではありません。記事の正確さ、読みやすい見出し、ページ固有のタイトル、正しいリンク、クロールできる配信状態なども整える必要があります。
また、SSRがXSSを自動的に防ぐわけではありません。Reactは通常の文字列をエスケープしますが、外部HTMLの挿入などは別途安全な処理が必要です。SSGにも依存パッケージや外部スクリプトの管理が必要です。
getStaticPropsやgetServerSidePropsの内部で秘密のAPIキーを使うことはできますが、返したpropsはブラウザへ送られます。秘密情報を返す値に含めないことが大切です。
次に読む記事と公式資料
具体的な導入はNext.jsをインストール、配置のルールはディレクトリ構成と役割、生成方式の実装はSSGの設定とSSRの設定で説明しています。
Next.jsを導入する前の確認
必要な機能を言葉にする
公開するページ、利用者ごとの表示、更新頻度、公開先を先に整理します。SSRやAPIが必要かによって配信環境が変わるため、画面のコードだけで導入方法を決めないようにします。
サンプルの前提を揃える
この記事はPages Routerを中心に説明しています。App Routerのapp/page.tsxを使うプロジェクトで試す場合は、データ取得やメタデータの書き方も対応する資料へ切り替えてください。