Reactjs Error Boundaries Suspense Portals Fallback Patterns
Definisi
Empat pola ini sering dipakai saat UI React mulai berurusan dengan kegagalan render, data yang belum siap, dan kebutuhan menampilkan elemen di luar hierarki DOM normal.
- Error Boundary: komponen React yang menangkap error saat render, lifecycle, dan constructor di subtree anak.
- Suspense: mekanisme untuk menahan bagian UI tertentu dan menampilkan fallback sampai dependensi asinkron siap.
- Portal: cara merender subtree React ke DOM node lain yang bukan parent visual langsungnya.
- UI fallback patterns: pola cadangan untuk loading, empty state, error state, skeleton, retry, dan graceful degradation.
Keempatnya sering dipakai bersama. Error boundary menjaga aplikasi tetap hidup saat render gagal. Suspense menjaga pengalaman loading tetap rapi. Portal membantu overlay seperti modal dan tooltip tetap berada di layer yang benar. Fallback pattern menjahit semuanya supaya UI tidak terasa putus.
Kapan dipakai
Pilih pola yang sesuai dengan masalahnya, jangan disatukan tanpa alasan.
Error Boundary
Pakai saat ada subtree yang bisa gagal render karena data, props, atau kode anaknya sendiri.
Contoh umum:
- dashboard dengan banyak widget independen
- halaman detail yang boleh gagal sebagian tanpa menjatuhkan seluruh layar
- integrasi komponen pihak ketiga yang kadang melempar error
- boundary tingkat route/section untuk isolasi kegagalan
Suspense
Pakai saat UI perlu menunggu sesuatu sebelum bagian tertentu ditampilkan.
Contoh umum:
- lazy loading komponen dengan
React.lazy() - data fetching yang mendukung suspense
- route segment yang ingin menampilkan fallback loading sementara
- nested loading state yang ingin dipisah per area UI
Portal
Pakai saat elemen perlu tetap dirender oleh React, tetapi secara visual harus keluar dari nesting normal.
Contoh umum:
- modal
- popover
- tooltip
- dropdown yang harus bebas dari clipping container
- toast atau floating layer tertentu
UI fallback patterns
Pakai saat UI tidak boleh berhenti hanya karena satu kondisi belum siap.
Contoh fallback yang sehat:
- skeleton untuk konten utama yang sedang menunggu data
- spinner untuk proses singkat
- empty state untuk data yang memang kosong
- error state dengan tombol retry
- partial content untuk area yang masih bisa ditampilkan
Cara kerja Error Boundary
Error Boundary menangkap error pada fase render subtree anak. Ini penting: ia bukan penangkap semua error global.
Error yang ditangkap
Error boundary menangkap error yang muncul saat:
- render komponen anak
- lifecycle method
- constructor komponen anak
Error yang tidak ditangkap
Error boundary tidak menangkap secara otomatis:
- event handler seperti
onClick - error async di
setTimeout, promise, ataufetchbiasa - error di server-side rendering
- error di dalam error boundary itu sendiri
Kalau error terjadi di event handler, tetap perlu try/catch atau penanganan error di layer lain.
Alur penangkapannya
Implementasi dasar
import React from 'react';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error, info) {
console.error('Boundary caught:', error, info);
// kirim ke logging service kalau perlu
}
render() {
if (this.state.hasError) {
return this.props.fallback ?? <p>Komponen gagal dimuat.</p>;
}
return this.props.children;
}
}Catatan penting
- Error boundary bekerja saat render fail, bukan sebagai pengganti validasi data.
- Fallback-nya harus sederhana dan aman. Jangan melempar error lagi di fallback.
- Tempatkan boundary sedekat mungkin dengan area yang rentan, tetapi jangan terlalu kecil sampai fallback jadi terlalu sering muncul.
Suspense dan loading states
Suspense mengubah cara kita memikirkan loading. Alih-alih semua loading ditaruh di satu state global, bagian UI bisa punya fallback sendiri.
Cara kerja Suspense
Saat anak di dalam <Suspense> belum siap, React menahan render bagian itu dan menampilkan fallback.
import { Suspense, lazy } from 'react';
const SettingsPanel = lazy(() => import('./SettingsPanel'));
export default function Page() {
return (
<Suspense fallback={<p>Memuat panel...</p>}>
<SettingsPanel />
</Suspense>
);
}Alur loading
Hubungan dengan loading state biasa
Loading state biasa biasanya eksplisit:
isLoadingdi state- conditional rendering manual
- skeleton/spinner diputuskan oleh developer
Suspense lebih cocok saat loading adalah bagian dari mekanisme render, bukan sekadar flag UI.
Praktik yang sehat:
- pakai Suspense untuk komponen atau data yang memang mendukung suspense
- pakai loading state manual untuk proses yang sifatnya interaktif, seperti submit form atau mutation
- jangan campur semua loading jadi satu state global kalau granularity-nya berbeda
Jebakan umum Suspense
- fallback terlalu besar sehingga satu panel menutupi seluruh halaman
- nested Suspense yang tidak dirancang, sehingga loading terasa loncat-loncat
- memakai Suspense untuk semua kasus, termasuk interaksi yang lebih cocok pakai pending state biasa
- fallback yang tidak konsisten dengan layout final, sehingga layout shift terasa besar
UI fallback patterns yang sehat
Fallback bukan cuma spinner. Yang bagus adalah fallback yang cocok dengan jenis ketidak-siapan UI.
1. Skeleton
Pakai untuk konten yang bentuk akhirnya sudah jelas.
Cocok untuk:
- kartu daftar
- artikel
- tabel
- panel detail
Skeleton bagus karena menjaga struktur layout tetap stabil.
2. Spinner atau progress indicator
Pakai untuk proses singkat atau saat struktur final belum penting.
Cocok untuk:
- loading kecil
- transisi singkat
- fetch yang cepat
Jangan dipakai terus-menerus untuk konten panjang karena memberi rasa “menggantung”.
3. Empty state
Pakai saat data memang kosong, bukan karena error.
Contoh:
- belum ada item
- filter terlalu sempit
- user belum membuat data apapun
Empty state sebaiknya menjelaskan alasan dan aksi berikutnya.
4. Error state
Pakai saat data gagal diambil atau render gagal di area tertentu.
Minimal berisi:
- pesan singkat
- kemungkinan penyebab umum
- tombol retry kalau relevan
5. Partial rendering
Pakai saat satu area gagal tetapi area lain masih bisa tampil.
Ini sering lebih baik daripada menjatuhkan seluruh halaman.
Portal dan layer DOM
Portal membuat React merender subtree ke node DOM lain. Di React, ini sering dipakai untuk overlay yang harus keluar dari flow normal layout.
Kapan portal dipakai
Pakai portal ketika elemen overlay:
- harus keluar dari
overflow: hidden - perlu melampaui stacking context tertentu
- harus berada di atas konten lain secara visual
- perlu diposisikan relatif ke viewport atau root layer tertentu
Contoh portal
import { createPortal } from 'react-dom';
export default function Modal({ open, children }) {
if (!open) return null;
return createPortal(
<div className="backdrop">
<div className="modal">{children}</div>
</div>,
document.getElementById('modal-root')
);
}Cara pikir yang benar
Portal mengubah posisi DOM output, bukan menghapus hubungan React tree.
Artinya:
- props tetap mengalir normal
- state tetap milik komponen React yang sama
- bubbling event React tetap mengikuti tree React, bukan selalu tree DOM visual
Jebakan umum pada portal dan modal layering
Bagian ini sering jadi sumber bug yang kelihatannya “CSS”, padahal akar masalahnya campuran DOM, stacking context, dan event.
1. Tertutup oleh stacking context
Portal biasanya dipakai untuk menghindari layering yang terjebak di parent dengan transform, opacity, position, atau z-index yang menciptakan stacking context baru.
Kalau modal tetap ketutup, cek:
- parent dengan
transform - parent dengan
overflow: hidden - z-index yang tidak konsisten
- root portal yang ternyata masih berada di dalam container bermasalah
2. Scroll lock yang tidak konsisten
Modal sering butuh body scroll lock. Kalau hanya overlay yang dipasang tanpa mengunci scroll, user bisa scroll latar belakang saat modal terbuka.
3. Fokus keyboard dan aksesibilitas
Portal tidak otomatis mengurus aksesibilitas.
Tetap perlu:
- fokus masuk ke modal saat dibuka
- fokus kembali ke trigger saat modal ditutup
aria-modal,role="dialog", dan label yang benar- trap fokus kalau modal interaktif
4. Event bubbling yang mengejutkan
Karena event React mengikuti tree React, klik di dalam portal bisa tetap memicu handler parent React tertentu jika struktur logiknya tidak dipikirkan dengan benar.
5. Layering antar portal
Kalau ada modal, dropdown, dan tooltip sekaligus, urutan layer harus didefinisikan. Jangan mengandalkan z-index acak.
Prinsip yang sehat:
- satu layer system yang konsisten
- token z-index yang jelas
- root portal terpusat
- komponen overlay tidak saling menebak level
Kombinasi pola yang sering dipakai bersama
Pola-pola ini jarang berdiri sendiri.
Error Boundary + Suspense
Dipakai bersama saat satu subtree bisa gagal sekaligus punya loading async.
- Suspense menangani resource belum siap
- Error boundary menangani render failure
Suspense + fallback skeleton
Dipakai saat loading state perlu terasa stabil dan tidak loncat layout.
Portal + modal + error boundary
Dipakai saat modal berisi komponen yang bisa gagal. Error boundary di dalam modal bisa mencegah kegagalan menutup seluruh aplikasi.
Portal + fallback overlay
Dipakai untuk toast, popover, dan modal supaya fallback/loading-nya tetap muncul di atas konten utama.
Contoh struktur yang sehat
<ErrorBoundary fallback={<p>Section gagal dimuat.</p>}>
<Suspense fallback={<SkeletonPanel />}>
<DashboardSection />
</Suspense>
</ErrorBoundary>Untuk modal:
<ModalPortal>
<ErrorBoundary fallback={<p>Modal error.</p>}>
<Suspense fallback={<ModalSkeleton />}>
<ModalContent />
</Suspense>
</ErrorBoundary>
</ModalPortal>Ringkasan keputusan cepat
- Pakai Error Boundary untuk kegagalan render subtree.
- Pakai Suspense untuk menunggu resource yang mendukung suspense.
- Pakai Portal untuk overlay yang harus keluar dari nesting visual normal.
- Pakai UI fallback patterns untuk menjaga UI tetap berguna saat belum siap, kosong, atau gagal.
Kalau dirancang dengan benar, keempatnya membuat UI React lebih tahan banting tanpa mengorbankan struktur komponen.
Referensi terkait
- React docs: Error Boundaries
- React docs: Suspense
- React docs: Portals
- Dokumen ini paling nyambung dengan topik render flow, hooks, loading UI, dan modal/overlay di layer CSS.