Badge Sertifikasi Tanpa Gambar Bikin next/image Crash
Field image dijadikan optional, tapi komponen langsung render next/image tanpa ngecek. Halaman About crash dengan error src missing. Ini fallback UI yang bener.
Tadi pagi saya iseng nambahin satu sertifikasi baru di file JSON locale halaman About klien. File about.id.json itu simpan array sertifikasi per bahasa, tiap entri punya nama, penerbit, dan image buat logo. Entri baru cuma punya nama plus penerbit, gak ada field image karena emang nggak punya file logo. Entri lain di array semua lengkap, jadi cuma satu ini yang beda. Pas saya jalanin dev server dan buka route /about, layar putih total. Console browser langsung merah nyala: Image is missing required "src" property. Aneh, padahal di tipe Certification saya udah buat image?: string biar jujur kalau data bisa kosong. Saya sempat curiga wrapper OptimizedImage kita yang salah teruskan prop ke next/image, atau next/image rusak di update terakhir. Dugaan itu meleset jauh.
Ternyata Bukan Salah Wrapper Kita
Saya buka kode OptimizedImage, komponen pembungkus yang cuma teruskan prop ke next/image. Dia nggak nambahin logika aneh, cuma set loader default dan bikin alt wajib, plus render di dalam div berukuran tetap pakai fill. Jadi error merah tadi murni dari dalam next/image. Cek di [dokumentasi next/image](https://nextjs.org/docs/app/api-reference/components/image), prop src memang wajib diisi; kalau undefined, komponen itu lempar error secara langsung, bukan sekadar gambar kosong. Saya juga nemu [issue GitHub tentang error src undefined](https://github.com/vercel/next.js/issues/29317) yang bilang kalau ini behavior by design, bukan bug. Next/image melakukan validasi runtime, bukan cuma pas compile. Waktu saya pikir wrapper kita rusak, saya malah buang waktu ngecek hal yang udah jalan. Pelajaran kecil: error message nunjukin persis komponen penanya, jangan langsung salahin abstraction sendiri.
TypeScript Cuma Jaga Waktu Kompilasi
Setelah tahu next/image memang segitu, saya ingat lagi soal tipe data. Di Certification, image?: string itu valid karena kami pakai [strictNullChecks TypeScript](https://www.typescriptlang.org/tsconfig/strictNullChecks.html). Artinya undefined punya tipe sendiri dan boleh masuk ke variabel optional. Masalahnya muncul pas data lewat JSON: properti yang nilainya undefined hilang sama sekali pas di-serialize, jadi sumber data malah nggak ngirim key image sama sekali. Di runtime, certification.image ya undefined. Ingat juga bahwa type system itu ilang pas runtime. JSX yang kita tulis dibundle jadi JavaScript biasa, dan undefined tetap undefined tanpa jubah. Next/image nggak peduli kalau di kode sumber kamu bilang image?, dia cuma lihat prop src pas komponen dirender. Jadi tipe jujur di compile time nggak otomatis nyelamatin halaman dari crash runtime. Kontrak data yang bener butuh penanganan di kedua sisi. Menariknya (bukan, bukan itu kata yang tepat), ini bukan kasus langka: cukup satu entri data yang bentuknya beda dari yang lain, halaman yang udah stabil berbulan-bulan langsung tumbang. Crash semacam ini jarang muncul pas development kalau data contoh kamu semuanya lengkap.
Fallback Itu Bagian dari Kontrak
Fix yang saya ambil simpel: jangan paksa OptimizedImage render kalau gambar nggak ada. Pakai [panduan conditional rendering React](https://react.dev/learn/conditional-rendering) dengan ternary di JSX. Kalau certification.image ada, tampilkan badge gambar; kalau nggak, kasih div fallback berisi ikon ShieldCheck dari lucide-react di atas background bg-primary/10. Kodenya ringkas:
{certification.image ? (
<OptimizedImage src={certification.image} alt={certification.name} />
) : (
<div className="bg-primary/10 flex h-12 w-12 items-center justify-center rounded">
<ShieldCheck className="h-6 w-6" />
</div>
)}
UI fallback ini bukan tempelan darurat. Menurut saya, dia bagian sah dari kontrak data optional. Tipe image?: string tanpa penanganan di render cuma mindahin crash dari compile ke runtime. Keduanya satu paket: tipe jujur plus cabang fallback. Commit itu juga nambah section kesejahteraan dan iconMap buat layanan baru (sparkles, concierge, folder, car) dengan fallback Settings kalau key nggak ketemu. Pola persis sama: data nyetir UI, entri yang hilang harus punya cabang pengganti, bukan melempar error. Saya pribadi pilih tetap simpan image optional dan tangani di UI, bukan paksa semua sertifikasi punya logo. Lebih jujur, dan halaman nggak putih gara-gara data kurang. Nyatanya, fallback yang dibikin rapi malah bikin tampilan lebih konsisten.
Baca juga soal bug yang lolos gara-gara test verify 0 fail dan kontrak di code block Shiki kalau mau dalami sisi testing-nya.