Penanganan error adalah fitur yang secara konsisten kurang diinvestasikan oleh pengembang sampai insiden produksi mengajarkan pelajarannya. Next.js App Router memperkenalkan sistem error boundary berbasis file yang menyelaraskan penanganan error dengan hierarki route Anda — peningkatan besar dari Pages Router di mana Anda harus menyiapkan error boundary secara manual.
Next.js App Router memiliki tiga file penanganan error: error.tsx menangkap error runtime di segmen route dan anak-anaknya. not-found.tsx menangkap panggilan notFound() dan route 404. global-error.tsx menangkap error di root layout (termasuk layout.tsx itu sendiri). error.tsx adalah Client Component (harus memiliki 'use client') dan menerima prop error (Error yang dilempar) dan prop reset (fungsi untuk mencoba render ulang).
error.tsx membungkus page.tsx, loading.tsx, dan layout anak dalam React error boundary, tapi TIDAK membungkus layout.tsx dalam segmen yang sama. Ini berarti error dalam root layout Anda tidak akan ditangkap oleh error.tsx tingkat root — Anda perlu global-error.tsx untuk itu. global-error.tsx menggantikan seluruh root layout saat dirender, sehingga harus menyertakan tag <html> dan <body>.
Next.js App Router error boundary hierarchy:
global-error.tsx ← catches errors in root layout.tsx (last resort)
└── app/layout.tsx
└── app/[locale]/layout.tsx
├── error.tsx ← catches errors in this route segment & children
│ └── app/[locale]/(pages)/layout.tsx
│ ├── error.tsx ← more specific boundary
│ │ ├── loading.tsx
│ │ ├── not-found.tsx ← catches notFound() calls
│ │ └── page.tsx ← your page component
│ └── ...
└── ...
Error file reference:
┌─────────────────┬────────────────────────────────────────────────┐
│ File │ When it renders │
├─────────────────┼────────────────────────────────────────────────┤
│ error.tsx │ Runtime error in page.tsx, loading, child layout│
│ not-found.tsx │ notFound() called or no route match │
│ global-error.tsx│ Error in root layout.tsx (must include html+body)│
└─────────────────┴────────────────────────────────────────────────┘
Data flow for Server Component errors:
Server Component throws → Next.js catches → sanitizes in prod
→ passes minimal info to nearest error.tsx (Client Component)
→ error.tsx renders fallback UI + reset buttonDari membangun penanganan error untuk dashboard ERP: nest error.tsx pada beberapa level hierarki route Anda, bukan hanya di root. Fitur akuntansi kritis yang error harus menampilkan pesan yang ditargetkan tentang fitur tersebut — bukan error tingkat aplikasi yang generik. Taruh error.tsx di layout dashboard Anda, di segmen fitur tertentu, dan simpan error.tsx root sebagai tangkapan terakhir.
Next.js menangani error sisi server secara berbeda dari error sisi klien. Server Component yang melempar error selama rendering menyebabkan Next.js meneruskan informasi error ke Client Component error.tsx terdekat. Dalam pengembangan, Anda melihat pesan error penuh dan stack trace. Dalam produksi, pesan error disanitasi — Next.js menampilkan pesan generik untuk mencegah kebocoran informasi server yang sensitif.
// app/[locale]/(pages)/error.tsx
"use client"
import { useEffect } from "react"
import * as Sentry from "@sentry/nextjs"
interface Props {
error: Error & { digest?: string }
reset: () => void
}
export default function Error({ error, reset }: Props) {
useEffect(() => {
// Capture client-side error (error boundary was activated client-side)
Sentry.captureException(error)
}, [error])
return (
<div className="flex flex-col items-center justify-center min-h-[400px] gap-6">
<div className="text-center">
<h2 className="text-2xl font-bold text-slate-100 mb-2">Something went wrong</h2>
<p className="text-slate-400 text-sm max-w-md">
{/* In production, error.message is sanitized for Server Component errors */}
An unexpected error occurred. Please try again or contact support
{error.digest ? ` (Error ID: ${error.digest})` : ""}.
</p>
</div>
<div className="flex gap-3">
<button onClick={reset} className="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600">
Try again
</button>
<a href="/" className="px-4 py-2 border border-slate-600 text-slate-300 rounded-lg hover:bg-slate-800">
Go home
</a>
</div>
</div>
)
}
// ─────────────────────────────────────────────────────────────────
// app/[locale]/(pages)/not-found.tsx (Server Component — no 'use client' needed)
import Link from "next/link"
export default function NotFound() {
return (
<div className="flex flex-col items-center justify-center min-h-[400px] gap-6">
<h2 className="text-6xl font-bold text-slate-100">404</h2>
<p className="text-slate-400">This page does not exist.</p>
<Link href="/" className="px-4 py-2 bg-blue-500 text-white rounded-lg">
Back to Home
</Link>
</div>
)
}
// ─────────────────────────────────────────────────────────────────
// Server Component — call notFound() for missing data
export default async function BlogPost({ params }: { params: { slug: string } }) {
const post = await prisma.post.findUnique({ where: { slug: params.slug } })
if (!post) {
notFound() // ← triggers not-found.tsx, returns proper 404 status
}
return <PostContent post={post} />
}
// ─────────────────────────────────────────────────────────────────
// instrumentation.ts — central Sentry capture for ALL Server Component errors
import * as Sentry from "@sentry/nextjs"
export function onRequestError(
error: Error,
request: { path: string; method: string },
context: { routeType: string }
) {
Sentry.captureException(error, {
extra: { path: request.path, method: request.method, routeType: context.routeType }
})
}Prop reset dalam error.tsx memanggil reset error boundary React — ia mencoba merender ulang anak-anak error boundary. Ini berfungsi dengan baik untuk error sementara (gangguan jaringan, ketidaktersediaan API sementara) tapi tidak akan memperbaiki error persisten dari data yang buruk atau bug. UI error.tsx yang baik: pesan error yang jelas, tombol 'Coba lagi' yang memanggil reset(), dan tautan 'Kembali ke beranda' yang navigasi menjauh.
error.tsx berjalan di sisi klien dan tidak bisa mengakses logging sisi server Anda (Pino, Winston, integrasi server Sentry). Error sisi klien di error.tsx harus ditangkap dengan layanan pelaporan error sisi klien (captureException Sentry, SDK browser Axiom). Tapi error sisi server yang menyebabkan boundary diaktifkan tidak akan otomatis dikirim ke integrasi server Sentry — Anda perlu menggunakan hook instrumentasi onRequestError Next.js 15 untuk menangkap semua error Server Component yang tidak tertangani secara terpusat.
not-found.tsx dirender saat notFound() dipanggil dari Server Component atau saat tidak ada route yang cocok dengan URL. Panggil notFound() dalam logika data fetching Anda: jika posting blog atau produk dengan slug yang diminta tidak ada di database, panggil notFound() daripada mengembalikan null dan merender halaman yang rusak. Kustomisasi not-found.tsx dengan navigasi situs Anda sehingga pengguna dapat menemukan konten lain.
Pengaturan penanganan error Next.js standar saya: error.tsx di level route group (pages) dengan kartu error bergaya, tombol reset, dan tautan beranda. not-found.tsx di root dengan saran pencarian dan halaman populer. global-error.tsx sebagai fallback minimal (hanya tombol untuk menyegarkan). Sentry untuk penangkapan error — hook onRequestError dalam instrumentation.ts menangkap semua error Server Component, dan Sentry.captureException dalam error.tsx menangkap error sisi klien.
Error boundary hanya sebaik pengujian Anda. Uji mereka: (1) Lempar secara sengaja di Server Component dan verifikasi error.tsx dirender. (2) Panggil notFound() dan verifikasi not-found.tsx dirender. (3) Lempar di root layout dan verifikasi global-error.tsx dirender. (4) Verifikasi bahwa error.message TIDAK ditampilkan di produksi. (5) Verifikasi Sentry menerima error dengan konteks yang tepat. Mode dev Next.js menampilkan detail error penuh; beralih ke build produksi untuk menguji pengalaman yang disanitasi.