Episode ini membahas penanganan error di Remix: error boundaries untuk menangkap kegagalan render, halaman error yang ramah pengguna, fallback UI untuk state pending dan error, serta pelaporan error di level request.

Test di episode 16 menjaga hal-hal yang diketahui. Tapi aplikasi juga gagal dengan cara yang tidak terduga: database down, API eksternal lambat, kode yang melempar error di jam 3 pagi. Episode 17 memastikan kegagalan itu tidak mengubah aplikasi menjadi layar putih yang kosong.
Remix memiliki model error handling yang dirancang baik: setiap route bisa mengekspor ErrorBoundary untuk menangkap error rendering, dan CatchBoundary pada Remix v2 untuk respons HTTP. Saat error terjadi, Remix me-render fallback yang kalian definisikan — bukan halaman kosong.
Episode 17 akan membangun strategi error lengkap: boundary per route, halaman error yang ramah, fallback untuk state pending, dan pelaporan error ke layanan monitoring.
ErrorBoundary menangkap error yang terjadi saat komponen di-render di route tersebut. Ia menerima error dan menampilkan fallback UI:
export function ErrorBoundary({ error }) {
console.error(error);
return (
<div>
<h1>Terjadi kesalahan</h1>
<p>Maaf, ada yang tidak beres saat memuat halaman ini.</p>
</div>
);
}ErrorBoundary menerima error dan me-render UI pengganti di dalam route itu. Error yang tidak ditangani route akan naik ke root ErrorBoundary di app/root.tsx.
Di Remix v2, CatchBoundary menangani respons 4xx dan 5xx yang dilempar loader, sementara ErrorBoundary menangani error tak terduga. Di Remix v3, CatchBoundary dihapus — semua error ditangani oleh ErrorBoundary tunggal dengan memeriksa response.status. Saat membaca tutorial lama, perhatikan versi yang dipakai agar tidak bingung.
Halaman error yang baik menjelaskan apa yang terjadi tanpa jargon teknis, memberi jalan keluar, dan tidak pernah menampilkan stack trace. Kombinasi yang aman: judul singkat, pesan ringkas, dan tombol kembali atau muat ulang.
import { Link } from "@remix-run/react";
export function ErrorBoundary({ error }) {
return (
<main>
<h1>Halaman tidak bisa dimuat</h1>
<p>Silakan coba lagi dalam beberapa saat.</p>
<Link to="/">Kembali ke beranda</Link>
</main>
);
}Aksi yang jelas seperti Link membuat pengguna tidak buntu di halaman error. Beranda adalah tujuan pulang yang selalu aman.
Definisikan ErrorBoundary di route yang rawan gagal — halaman detail, dasbor — dan biarkan root menyediakan jaring pengaman terakhir. Pendekatan berlapis memberi kontrol lokal tanpa kehilangan keamanan global.
useFetcher mengekspos fetcher.state dengan nilai idle, submitting, dan loading. UI bisa memakai ini untuk menampilkan indikator:
import { useFetcher } from "@remix-run/react";
export default function FormKirim() {
const fetcher = useFetcher();
const sedangKirim = fetcher.state !== "idle";
return (
<fetcher.Form method="post">
<button type="submit" disabled={sedangKirim}>
{sedangKirim ? "Mengirim..." : "Kirim"}
</button>
</fetcher.Form>
);
}fetcher.state berubah sesuai siklus pengiriman; tombol nonaktif mencegah klik ganda. Feedback visual seperti ini membuat antrean terasa dikelola, bukan dibiarkan.
Untuk data yang di-stream dengan defer, Await menyediakan fallback sementara data belum siap. Gabungkan dengan state error di dalam Await untuk menampilkan pesan bila promise gagal. Setiap promise yang di-stream butuh fallback pending dan error.
Error di loader dan action terjadi di server — di sanalah kalian mencatatnya. Jangan hanya mencatat di console; kirim ke layanan seperti Sentry. Praktik mencatat error lengkap dengan request dan pengguna akan dibahas lagi di episode 22.
export async function loader() {
try {
const data = await ambilData();
return data;
} catch (error) {
console.error("Gagal memuat data", error);
throw error;
}
}Catat error di titik paling dekat sumbernya, lalu rethrow agar boundary menanganinya. Pelaporan yang baik memberi konteks: kapan, di route mana, dan dengan request seperti apa.
Error yang nyaman dilihat adalah tanda aplikasi dewasa. Simpan catatan error sebagai sumber belajar tim, bukan aib. Setiap kegagalan yang ditangani dengan baik adalah pelajaran yang terdokumentasi.
Episode 17 membuat aplikasi kalian kuat menghadapi kegagalan: ErrorBoundary per route dan global, halaman error yang ramah pengguna, fallback untuk state pending dan error, serta pelaporan error di level request. Kegagalan kini menjadi kejadian yang dikelola, bukan krisis.
Inti yang harus dibawa pulang:
Di episode 18 selanjutnya kita akan membahas architecture dan maintainability — struktur folder untuk aplikasi Remix yang skalabel, organisasi feature modules dan route, shared utilities serta typed contracts, dan cara menjaga kode tetap terpelihara. Error sudah ditangani; sekarang pastikan kode yang menangani itu mudah dirawat.