notFound()とnot-found.tsxの関係とは? 404ページの仕組みを初心者向けにやさしく解説
Next.jsで記事詳細ページを作っていると、
notFound()という関数と
not-found.tsxというファイルが登場します。
名前がよく似ているため、
「どちらが404ページなの?」「両方必要なの?」と
最初は迷いやすい部分です。
今回は、microCMSの記事が存在しなかった場合を例に、
2つの役割と処理の流れを整理します。
2つの名前が似ていて分かりにくい
Next.jsのコードを見ていると、 次のような処理が登場します。
if (!article) {
notFound();
}
さらに、appフォルダー内には 次のようなファイルを作ります。
どちらが404ページなの?
notFound()もnot-found.tsxも、
名前だけを見ると404を表示するものに見えます。
しかし、2つは同じ役割ではありません。
「このページは見つからない」と Next.jsへ伝える関数です。
ユーザーに表示する 実際の画面を定義するファイルです。
そもそも404ページとは?
404ページは、 ユーザーがアクセスしたURLに対応するページやデータが 見つからないときに表示するページです。
お探しのページは見つかりませんでした。
たとえば、次のような場合に404ページが必要になります。
存在しない文字列をURLへ入力した。
以前存在した記事がmicroCMSから削除された。
URLの記事IDに該当するデータがない。
外部サイトに古いURLが残っている。
ページが存在しないのに、 空白画面やプログラムのエラーを表示すると、 ユーザーは何が起きたのか分かりません。
ページが見つからないことを明確に伝え、 トップページや記事一覧など、 次に移動できる場所を案内します。
notFound()とは?
notFound()は、
Next.jsが提供する関数です。
ページの処理中にnotFound()を実行すると、 そのルートで通常のページ表示を中断し、 対応するnot-foundファイルのUIへ切り替えます。
データが存在しない場合など、 条件に応じて404表示へ切り替えます。
最初にimportする
import { notFound } from "next/navigation";
必要な条件で実行する
if (!article) {
notFound();
}
このコードは、 articleが存在しなかった場合に notFound()を実行するという意味です。
notFound()は実行後に通常のページ処理を続けないため、
基本的にreturn notFound();と書く必要はありません。
not-found.tsxとは?
not-found.tsxは、
404時にユーザーへ表示するUIを作るためのファイルです。
メッセージ、リンク、イラストなどを 通常のReactコンポーネントと同じように記述します。
import Link from "next/link";
export default function NotFound() {
return (
<main>
<p>404</p>
<h1>
お探しのページは見つかりませんでした
</h1>
<p>
URLが変更されたか、
ページが削除された可能性があります。
</p>
<Link href="/">
トップページへ戻る
</Link>
</main>
);
}
ファイル名は
not-found.tsxであり、
コンポーネント名は自由に付けられます。
notFound()とnot-found.tsxの関係
2つは、次の順番で連携します。
記事詳細URLが開かれる。
microCMSへ記事を問い合わせる。
取得結果を確認する。
ユーザーへ404画面を見せる。
notFound()
きっかけ
404へ切り替える
not-found.tsx
表示内容
404画面を描画する
基本的な実装方法
App Routerで記事詳細ページを作る場合は、 次のような構成になります。
記事詳細ページ
import { notFound } from "next/navigation";
type Props = {
params: Promise<{
id: string;
}>;
};
export default async function BlogDetail({
params,
}: Props) {
const { id } = await params;
const article = await getArticle(id);
if (!article) {
notFound();
}
return (
<article>
<h1>{article.title}</h1>
<div
dangerouslySetInnerHTML={{
__html: article.content,
}}
/>
</article>
);
}
404ページ
import Link from "next/link";
export default function NotFoundPage() {
return (
<main className="not-found-page">
<p className="not-found-page__number">
404
</p>
<h1>
お探しのページは見つかりませんでした
</h1>
<p>
ページが削除されたか、
URLが変更された可能性があります。
</p>
<Link href="/blog">
記事一覧へ戻る
</Link>
</main>
);
}
microCMSの記事詳細ページで使う
microCMSの記事詳細ページでは、 URLの記事IDを使ってデータを取得します。
/blog/
abc123
しかし、URLに指定されたIDの記事が microCMSに存在するとは限りません。
取得処理で404を判定する例
import { notFound } from "next/navigation";
import { client } from "@/libs/microcms";
async function getArticle(id: string) {
try {
return await client.get({
endpoint: "blogs",
contentId: id,
});
} catch {
return null;
}
}
export default async function Page({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const article = await getArticle(id);
if (!article) {
notFound();
}
return (
<article>
<h1>{article.title}</h1>
</article>
);
}
「記事を取得できなかったら必ず404」とは限りません。 通信障害や設定ミスまで404として扱わないように、 実際の取得処理ではエラー内容を確認して設計します。
not-found.tsxを置く場所
not-found.tsxは、 appフォルダーのルートだけでなく、 特定のルートセグメント内にも配置できます。
近いルートセグメントにあるnot-foundファイルを使うことで、 ブログ用、商品用など、 コンテンツに合った案内を表示できます。
セグメントごとに404画面を変える
サイト全体の404と、 ブログ記事が見つからない場合では、 ユーザーに案内したい内容が異なります。
トップページへ戻る
新着記事を見る
import Link from "next/link";
export default function BlogNotFound() {
return (
<main>
<p>404</p>
<h1>
お探しの記事は見つかりませんでした
</h1>
<p>
記事が削除されたか、
URLが変更された可能性があります。
</p>
<Link href="/blog">
記事一覧を見る
</Link>
</main>
);
}
存在しないURLとnotFound()の違い
404が表示される場面は、 大きく2種類に分けられます。
/unknown-page
app内に対応するページがなく、 Next.jsのルーターが見つけられない状態です。
/blog/unknown-id
記事詳細ページはあるものの、 指定された記事が存在しない状態です。
Next.jsには、通常のnot-foundファイルに加え、
アプリ全体で一致しないURLを処理するための
global-not-found.jsという仕組みもあります。
一般的なApp Routerのサイトでは、 まずapp/not-found.tsxとnotFound()の関係を理解すれば 基本的な404処理を組み立てられます。
error.tsxとの違い
404ページとエラーページは、 どちらも通常のページを表示できないときに使います。
ただし、意味は異なります。
正常に確認した結果、 対象データがなかった。
予期しない例外などで、 ページを正常表示できなかった。
APIの接続障害やプログラムの不具合まで 「ページがありません」と表示すると、 本当の原因が分かりにくくなります。
404ページに入れたい内容
404ページは、 「見つかりませんでした」と伝えるだけでなく、 ユーザーがサイト内で迷わないように案内します。
ページが見つからない状態だと伝えます。
削除やURL変更の可能性を案内します。
トップや記事一覧への導線を用意します。
通常ページと同じ世界観で整えます。
404ページの実装例
import Link from "next/link";
export default function NotFoundPage() {
return (
<main className="not-found">
<p className="not-found__number">
404
</p>
<h1>
お探しのページは見つかりませんでした
</h1>
<p>
URLをご確認いただくか、
以下のリンクから目的のページをお探しください。
</p>
<div className="not-found__links">
<Link href="/">
トップページへ戻る
</Link>
<Link href="/blog">
記事一覧を見る
</Link>
</div>
</main>
);
}
よくあるつまずき
import { notFound } from "next/navigation";
notFound.tsx
→
not-found.tsx
if (!article) {
notFound();
}
データの有無を確認したうえで実行します。
記事が存在しない場合と、 API通信そのものが失敗した場合を分けて考えます。
今回説明している方法はApp Router用です。 appフォルダー内へ配置します。
<Link href="/">
トップページへ戻る
</Link>
データが存在しない場合はnot-found、 予期しない処理エラーはerrorとして整理します。
まとめ:関数が切り替え、ファイルが表示する
notFound()とnot-found.tsxは、 どちらも404表示に関係しています。
ただし、 一方は処理を開始する関数、 もう一方は画面を作るファイルです。
今回覚えておきたいこと
- notFound()はNext.jsが提供する関数
- notFound()は通常のページ表示を中断する
- not-found.tsxは404画面を定義するファイル
- データが存在しない場合にnotFound()を実行する
- notFound()の実行後にnot-foundのUIが表示される
- not-found.tsxはappフォルダー内へ置く
- ルートセグメントごとに404画面を用意できる
- 404と予期しないエラーは分けて考える
- 404ページには戻るためのリンクを用意する
if (!article) { notFound(); }
export default function NotFound()
最初は、notFound()とnot-found.tsxが 同じものの別の書き方に見えました。
しかし、処理と画面を分けて考えると、 2つの役割はとてもシンプルです。
not-found.tsxがユーザーへ画面を見せます。