error.tsx membungkus page.tsx dan layout anak dalam React error boundary, tetapi TIDAK membungkus layout.tsx pada segmen route yang sama, sehingga error yang terjadi di dalam root layout tidak akan tertangkap oleh error.tsx tingkat root. global-error.tsx hadir khusus untuk menangkap error root layout tersebut dan menggantikan seluruh root layout saat dirender, sehingga harus menyertakan tag html dan body sendiri. Untuk sebagian besar aplikasi, error.tsx pada beberapa level hierarki route sudah mencakup mayoritas kasus, dengan global-error.tsx sebagai fallback terakhir.
Di produksi, Next.js mengsanitasi pesan error sisi server sebelum meneruskannya ke Client Component error boundary untuk mencegah kebocoran informasi sensitif server kepada pengguna. Detail pesan dan stack trace yang terlihat saat pengembangan akan dihapus, menyisakan hanya placeholder generik. Mengandalkan error.message di UI error.tsx akan diam-diam menampilkan konten yang tidak berarti di produksi, sehingga komponen sebaiknya menampilkan pesan tetap yang ramah pengguna.
Prop reset memanggil reset error boundary React, yang mencoba merender ulang anak-anak boundary tanpa navigasi halaman penuh. Untuk error sementara seperti gangguan jaringan singkat atau ketidaktersediaan API sementara, ini bisa berhasil dan memulihkan UI tanpa pengguna kehilangan posisi mereka. Namun, reset tidak akan memperbaiki error persisten yang disebabkan oleh data yang buruk atau bug kode — boundary akan langsung menangkap error lagi, sehingga tautan 'Kembali ke beranda' sebagai alternatif juga direkomendasikan.
error.tsx berjalan di sisi klien dan tidak memiliki akses ke logging sisi server, sehingga error server yang memicu boundary tidak secara otomatis diteruskan ke integrasi server Sentry. Pendekatan yang direkomendasikan adalah menggunakan hook instrumentasi onRequestError (tersedia di Next.js 15) di dalam instrumentation.ts untuk menangkap semua error Server Component yang tidak tertangani di sisi server secara terpusat, sementara Sentry.captureException di dalam error.tsx menangkap error sisi klien. Setup dua jalur ini memastikan setiap error — terlepas dari asalnya — dilaporkan dengan konteks yang tepat.
Memanggil notFound() adalah pilihan yang tepat setiap kali resource dengan identifikasi yang diminta tidak ada di database — misalnya, posting blog atau produk dengan slug yang tidak dikenal. Ini memicu not-found.tsx, memberikan pengguna pengalaman 404 yang proper dengan opsi navigasi, bukan halaman yang rusak atau kosong akibat merender data null. not-found.tsx yang dikustomisasi dengan baik beserta navigasi situs mencegah pengguna terjebak di jalan buntu.
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.