Suspense & Lazy — React

Pas user buka aplikasi pertama kali, browser download SEMUA JavaScript bundle — kalau gede (1-2MB), waktu loading awal lambat (terutama di koneksi lemah). Tapi

Pas user buka aplikasi pertama kali, browser download SEMUA JavaScript bundle — kalau gede (1-2MB), waktu loading awal lambat (terutama di koneksi lemah). Tapi user mungkin gak butuh semua kode itu sekaligus — fitur admin gak dipake user biasa, halaman Settings cuma sekali-kali dibuka. Kenapa download semua? Solusi: code splitting — bundle dipecah jadi chunk kecil, tiap chunk di-load on-demand. React.lazy + Suspense = pattern bawaan React buat ini. Halaman pertama lebih ringan, user happy, fitur lain di-load pas dibutuhkan.

💡 Real impact. Project hypersheets pas pakai lazy per route turun bundle awal dari 800KB jadi 200KB. First Contentful Paint dari 2.8s jadi 1.2s di mobile slow 3G. Diff yang user rasain langsung.

React.lazy — Dynamic Import

import { lazy, Suspense } from "react"

// Component di-import dynamic — chunk terpisah
const HeavyComponent = lazy(() => import("./HeavyComponent"))

function App() {
  return (
    <Suspense fallback={<Spinner />}>
      <HeavyComponent />
    </Suspense>
  )
}

import("./HeavyComponent") = dynamic import, return Promise. Build tool (Vite/Webpack) auto pisahin jadi chunk terpisah.

Route-based Code Splitting (Pattern Paling Umum)

const Home = lazy(() => import("./pages/Home"))
const About = lazy(() => import("./pages/About"))
const Dashboard = lazy(() => import("./pages/Dashboard"))

function App() {
  return (
    <Suspense fallback={<PageSpinner />}>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/dashboard" element={<Dashboard />} />
      </Routes>
    </Suspense>
  )
}

User di Home → cuma load Home.chunk.js. Klik /dashboard → load Dashboard.chunk.js saat itu. Bundle awal kecil, navigasi tetap smooth (browser cache chunk-nya).

Suspense Lebih dari Loading State

Di React 19+, Suspense bisa handle:

<Suspense fallback={<Spinner />}>
  <DataComponent />  {/* yang fetch data via use() */}
  <LazyImage src={url} />
  <LazyComponent />
</Suspense>

Suspense = "tunjukin fallback sampai semua child READY (loaded + data ready)". Pattern unified untuk loading state.

Conditional Lazy Loading

function App({ isAdmin }) {
  // AdminPanel cuma di-load kalau user admin
  return (
    <div>
      <PublicContent />
      {isAdmin && (
        <Suspense fallback={<Spinner />}>
          <AdminPanel />  {/* lazy() */}
        </Suspense>
      )}
    </div>
  )
}

User non-admin gak pernah download AdminPanel chunk.

🎭 Analogi sehari-hari

React.lazy + Suspense itu kayak buffet vs prasmanan. Buffet = semua makanan disajikan sekaligus pas tamu masuk (download all bundle = lambat). Prasmanan = makanan disiapin di stand-stand, tamu ambil yang dia mau (lazy load per chunk). Tamu yang cuma mau sushi gak nungguin tukang masak nasi padang. Hasilnya: tamu masuk gak nungguin lama, dapur kerja efisien (download sesuai kebutuhan), pengalaman makan lebih cepat. Suspense = "tanda tunggu sebentar" pas chef lagi masak ulang di stand tertentu.

⚠️ Jebakan yang sering ditemui

🎯 Lazy load apa, kapan?

  • Per route page (Home, Dashboard, Settings) → ✓ standar default
  • Modal/dialog yang jarang dibuka → ✓ load saat dibuka
  • Heavy feature (chart library, video player) → ✓ load saat fitur dipake
  • Admin/internal-only sections → ✓ user biasa gak download
  • Dependency besar (lodash full, moment.js, charting lib) → ✓ atau ganti yang lebih ringan
  • Component kecil yang dipake di banyak tempat → ✗ overhead > benefit
  • Above-the-fold content → ✗ user butuh segera, jangan delay

Aturan: split BIG + RARELY USED chunks. Small + always used = biarin di main bundle.

TL;DR: React.lazy(() => import("./X")) + <Suspense fallback={<Spinner />}> = code splitting bawaan. Pecah bundle JavaScript jadi chunk yang di-load on-demand. Route-based splitting = pattern paling umum (per page = chunk). Bundle awal jadi kecil = first paint cepat. Wajib Suspense parent. Default export di lazy component. Component kecil yang sering dipake = JANGAN lazy load (overhead > benefit).

Yang akan kamu pelajari