Next.js の 404 とエラー画面。最小構成サンプル
うまくいかない形は2つあります。
動いているものを触る/demo/notfoundファイル構成
並んでいるのは、上の画面をいま動かしている本物のファイルです。名前を押すと中身が出ます。
- app/
- [id]/
全ページの外側。本物のプロジェクトでは html と body をここで書きます
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "404 とエラーの最小構成",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return <main>{children}</main>;
}- .tsx
- 画面を返すファイル。HTML のような書き方ができます
- .ts
- 画面を返さないファイル。処理とデータだけを置きます。ここに画面を書くと原因を名指ししないエラーで落ちます
- Build
- ビルド時に一度だけ動きます
- Server
- アクセスのたびにサーバーで動きます
- Browser
- 利用者の手元で動きます
「無い」と「壊れた」は別のこと
| どんなとき | 置くファイル | use client |
|---|---|---|
| 探したが無かった | not-found.tsx | できない |
| 途中で失敗した | error.tsx | できる |
同じ「うまくいかない」でも、利用者への意味がまったく違います。無いものは何度やっても無い。失敗はもう一度やれば通るかもしれない。だから片方には「もう一度」が要り、もう片方には要りません。
無いものは、無いと言う
const { id } = await params
if (id !== "ok") notFound()
notFound() を呼ぶと、Next.js が not-found.tsx を探して出します。自分で <NotFound /> を書く必要はありません。
実機で確かめました。/demo/notfound/zzz は HTTP 404 を返します。画面が404らしく見えるだけでなく、応答そのものが 404 です。
片方だけ use client が要る
// use client は要らない export default function NotFound() { return <p>その id はありません。</p> }
描くだけ。サーバー側のままでよい。
'use client' export default function Error({ error, reset }) { return <button onClick={reset}>もう一度</button> }
押せるものを持つ。書かないとビルドが落ちる。
失敗は、サーバーで起こさない
このサンプルの「わざと失敗させる」ボタンは、ブラウザ側で失敗しています。
'use client'
export function Boom() {
const [broken, setBroken] = useState(false)
if (broken) throw new Error('わざと失敗させています')
...
}
押すとどうなるか
実機で確かめた動きです。
押す前 2つの「うまくいかない」を見比べます …
押した後 うまくいきませんでした。 / もう一度
もう一度 2つの「うまくいかない」を見比べます … ← 元に戻る
reset は、失敗した部分をもう一度描き直させる関数です。ページを開き直すのとは違い、そこだけをやり直します。
- Browser描いている途中で失敗する
その部分から上へ、いちばん近い error.tsx が受け止める。
- Browsererror.tsx が出る
ページ全体は生きている。ヘッダーもフッターも残る。
- Browserもう一度を押す
reset が呼ばれ、その部分だけ描き直される。
digest は、サーバーで失敗したときだけ
error.tsx が受け取る error には digest が付くことがあります。このサンプルでは出ません。ブラウザ側で失敗しているからです。
サーバー側で失敗したときは、本番ビルドでは例外の中身が届かず、番号だけが付きます。中身を知るにはサーバーのログを見ます。詳しくはServer Function の呼び方にあります。
ここから足していくとき
- 記録する。どこで失敗したかをサーバーへ送ります。ただし利用者の入力は送らない
- 案内を出す。「時間をおいて」なのか「問い合わせて」なのかで、書くことが変わります
- 全体を覆う。
app直下のglobal-error.tsxは、レイアウトごと壊れたときに出ます
まず「無い」と「壊れた」を分けてください。分けずに1つの画面で済ませると、利用者は次に何をすればよいか分かりません。
入れていないもの
このまま本番に持っていかないでください。省いてあるのは次の点です。
- 失敗の記録。どこで失敗したかをサーバーに残していません
- 利用者への案内。問い合わせ先や再試行の回数を出していません
- サーバー側で失敗させる入口。公開の画面に置くと、誰でもエラーを起こせます
- 全体を覆う error。app 直下の global-error は置いていません
出典
- nextjs.org/docs/app/api-reference/file-conventions/not-found
- nextjs.org/docs/app/api-reference/file-conventions/error
- nextjs.org/docs/app/api-reference/functions/not-found
このページの内容は上記の一次情報を基準に書いています。個人ブログや二次情報を根拠にしていません。