Code Splitting dan Lazy Loading dengan Claude Code
Terapkan code splitting React/Next.js dengan Claude Code: contoh, langkah, jebakan, verifikasi, dan CTA.
Saat aplikasi React atau Next.js terasa lambat, penyebabnya sering ada pada JavaScript awal yang terlalu besar. Pengunjung hanya ingin membaca halaman atau menekan CTA, tetapi browser ikut mengunduh panel admin, editor, grafik, dan widget yang belum diperlukan.
Code splitting berarti membagi JavaScript menjadi file kecil bernama chunk. Lazy loading berarti chunk tersebut baru dimuat saat benar-benar dibutuhkan. Tujuannya bukan membuat semua hal tertunda, tetapi menjaga layar pertama tetap ringan.
Artikel ini menunjukkan cara memberi tugas yang jelas kepada Claude Code: React.lazy, Suspense, dynamic import(), next/dynamic, pemisahan route/page, risiko hydration, dan checklist verifikasi. Untuk alur performa yang lebih lengkap, baca juga bundle analysis, tree shaking, dan performance optimization.
Istilah dasar
bundle adalah paket JavaScript yang dikirim ke browser. chunk adalah file kecil hasil pemisahan. dynamic import, seperti import("./Chart"), memuat modul saat runtime. Suspense adalah boundary React yang menampilkan fallback ketika komponen lazy sedang dimuat.
hydration adalah proses ketika JavaScript client memasang event dan state ke HTML yang sudah dirender server. Jika output server dan client berbeda, muncul hydration error. Risiko umum datang dari membaca window, localStorage, waktu saat ini, atau nilai acak terlalu awal.
| Kandidat | Alasan cocok | Perhatian |
|---|---|---|
| Panel admin | Tidak dibuka semua pengunjung | State izin dan loading harus jelas |
| Grafik, peta, editor | Dependensi biasanya berat | Siapkan tinggi agar layout tidak melompat |
| Modal dan wizard | Dibutuhkan setelah aksi | Prefetch jika klik pertama terasa lambat |
| Video, audio, pencarian | Sering berada di bawah fold | Aksesibilitas dan pesan error |
Beri batasan ke Claude Code
Jangan hanya meminta “lakukan code splitting”. Tuliskan tujuan, file target, area yang tidak boleh rusak, dan cara verifikasi.
Goal:
- Lazy-load UI berat yang tidak dibutuhkan pada layar pertama.
- Jangan pindahkan hero, artikel, navigasi, atau CTA ke balik lazy loading.
- Gunakan React.lazy/Suspense atau next/dynamic sesuai framework.
Targets:
- src/features/reports/ReportsPanel.tsx
- src/features/editor/RichEditor.tsx
- app/admin/page.tsx
Verification:
- npm run lint
- npm run build
- Cek initial JS dan lazy chunks di tab Network.
- Cek tampilan mobile agar fallback tidak menutup CTA.
Dengan scope seperti ini, Claude Code lebih kecil kemungkinannya memecah komponen yang justru penting untuk SEO dan konversi.
React.lazy dan Suspense
Dokumentasi React menjelaskan bahwa lazy menunda pemuatan kode komponen sampai komponen pertama kali dirender. Suspense menampilkan fallback selama proses itu. Deklarasikan komponen lazy di level module, bukan di dalam komponen lain.
// src/App.tsx
import { Suspense, lazy, useState } from "react";
const ReportsPanel = lazy(() => import("./ReportsPanel"));
function PanelSkeleton() {
return (
<div role="status" aria-live="polite" style={{ minHeight: 180 }}>
Memuat laporan...
</div>
);
}
export default function App() {
const [showReports, setShowReports] = useState(false);
return (
<main>
<h1>Dashboard</h1>
<button type="button" onClick={() => setShowReports(true)}>
Tampilkan laporan
</button>
{showReports ? (
<Suspense fallback={<PanelSkeleton />}>
<ReportsPanel />
</Suspense>
) : (
<p>UI laporan yang berat baru dimuat saat diperlukan.</p>
)}
</main>
);
}
// src/ReportsPanel.tsx
const rows = [
{ label: "Artikel selesai dibaca", value: "68%" },
{ label: "Klik CTA", value: "4.2%" },
{ label: "Kunjungan konsultasi", value: "1.1%" },
];
export default function ReportsPanel() {
return (
<section aria-label="Laporan">
<h2>Laporan konversi</h2>
<ul>
{rows.map((row) => (
<li key={row.label}>
{row.label}: {row.value}
</li>
))}
</ul>
</section>
);
}
lazy mengharapkan default export. Jika modul hanya punya named export, bungkus hasil import-nya.
// src/lazyNamed.tsx
import { lazy, type ComponentType } from "react";
export function lazyNamed<TModule, TName extends keyof TModule>(
loader: () => Promise<TModule>,
name: TName
) {
return lazy(async () => {
const module = await loader();
return {
default: module[name] as ComponentType,
};
});
}
// src/AnalyticsSlot.tsx
import { Suspense } from "react";
import { lazyNamed } from "./lazyNamed";
const BarChart = lazyNamed(() => import("./charts"), "BarChart");
export function AnalyticsSlot() {
return (
<Suspense fallback={<p>Memuat grafik...</p>}>
<BarChart />
</Suspense>
);
}
Dynamic import di Next.js
Next.js sudah memisahkan kode berdasarkan pages dan routes. Gunakan next/dynamic ketika sebuah Client Component berat atau bergantung pada API browser. Letakkan import() di dalam dynamic() dan deklarasikan di level module.
// app/admin/EditorSlot.tsx
"use client";
import dynamic from "next/dynamic";
const RichEditor = dynamic(() => import("./RichEditor"), {
ssr: false,
loading: () => (
<p aria-live="polite" style={{ minHeight: 160 }}>
Memuat editor...
</p>
),
});
export default function EditorSlot() {
return <RichEditor initialMarkdown="# Draft" />;
}
// app/admin/page.tsx
import EditorSlot from "./EditorSlot";
export default function AdminPage() {
return (
<main>
<h1>Editor artikel</h1>
<p>Teks dan CTA muncul lebih dulu. Hanya editor berat yang ditunda.</p>
<EditorSlot />
</main>
);
}
ssr: false cocok untuk editor browser-only, tetapi tidak cocok untuk artikel, harga, FAQ, atau CTA utama. Konten seperti itu harus terlihat dalam HTML server.
Pemisahan route dan page
Mulai dari batas route sebelum memecah komponen kecil. Di Next.js, file seperti app/reports/page.tsx dan app/settings/page.tsx sudah menjadi batas alami. Di React Router, route component bisa dibuat lazy.
// src/AppRouter.tsx
import { Suspense, lazy } from "react";
import { BrowserRouter, Link, Route, Routes } from "react-router-dom";
const HomePage = lazy(() => import("./pages/HomePage"));
const ReportsPage = lazy(() => import("./pages/ReportsPage"));
const SettingsPage = lazy(() => import("./pages/SettingsPage"));
function RouteFallback() {
return <p aria-live="polite">Memuat halaman...</p>;
}
export default function AppRouter() {
return (
<BrowserRouter>
<nav>
<Link to="/">Home</Link>
<Link to="/reports">Reports</Link>
<Link to="/settings">Settings</Link>
</nav>
<Suspense fallback={<RouteFallback />}>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/reports" element={<ReportsPage />} />
<Route path="/settings" element={<SettingsPage />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
Jangan membungkus seluruh landing page dalam satu Suspense besar. Judul, value proposition, dan CTA harus tetap muncul cepat.
Use case praktis
Pertama, dashboard SaaS. Shell dan navigasi muncul segera, sedangkan analytics admin, audit log, export CSV, dan chart library dimuat setelah role check.
Kedua, platform editorial atau kursus. Artikel, judul pelajaran, dan CTA pembelian harus render lebih dulu. Markdown editor, image cropper, dan preview pane bisa ditunda.
Ketiga, landing page dengan peta, video, kalkulator, atau grafik. Pesan utama muncul cepat, widget berat dimuat setelah scroll atau klik. Untuk media UI, lihat video player dan accessibility.
Keempat, modal penawaran atau checkout wizard. Tombol langsung tersedia, sedangkan form multi-step dimuat saat diperlukan.
Jebakan umum
- Terlalu banyak splitting membuat request kecil bertambah dan interaksi terasa lambat.
- Mendeklarasikan
lazyataudynamicdi dalam komponen bisa mereset state. - Fallback tanpa tinggi tetap menyebabkan layout shift dan bisa menutup CTA.
- Hydration mismatch terjadi ketika server dan client merender output berbeda.
- Konten SEO atau monetisasi tidak sebaiknya disembunyikan di balik
ssr: false.
Verifikasi
Claude Code review request:
- Pastikan lazy/dynamic dideklarasikan di level module.
- Cek default export vs named export.
- Cek apakah boundary Suspense terlalu luas.
- Pastikan ssr:false tidak menyembunyikan copy SEO atau CTA.
- Cari risiko window/date/random/localStorage.
- Jelaskan cara membandingkan initial JS, lazy chunks, dan jumlah request.
npm run lint
npm run build
Buka DevTools, filter Network ke JS, lalu reload. Chunk editor atau admin tidak boleh muncul pada initial load. Setelah report, editor, atau modal dibuka, chunk terkait baru muncul.
Link resmi dan CTA
Rujukan resmi: React lazy, React Suspense, Next.js Lazy Loading, dan Next.js Layouts and Pages.
Untuk workflow Claude Code yang berulang, mulai dari free Claude Code cheatsheet. Untuk prompt implementasi dan review yang bisa dipakai ulang, gunakan 50 Claude Code Prompt Templates. Untuk tim yang perlu aturan repo, CLAUDE.md, dan review performa, gunakan training dan konsultasi.
Catatan praktik
Hasil paling stabil muncul ketika konten yang harus dilindungi diputuskan sejak awal: first-screen copy, navigasi, dan CTA tetap server-rendered; laporan dan editor pindah ke lazy chunks. Permintaan samar seperti “buat lazy” cenderung menghasilkan Suspense terlalu luas dan ssr: false yang tidak perlu.
PDF gratis: cheatsheet Claude Code
Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.
Kami menjaga datamu dan tidak mengirim spam.
Tentang penulis
Masa
Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.
Artikel terkait
Permission safety ladder Claude Code: perluas akses tanpa kehilangan kontrol
Naik dari read-only ke edit terbatas, command bukti, dan cek deploy dengan kontrol yang jelas.
Claude Code Small PR Proof Pack: perubahan kecil yang mudah direview
Paket bukti untuk PR Claude Code: diff, check, URL publik, jalur CTA, dan rollback.
Review gate Claude Code sebelum commit: diff, test, URL publik, dan CTA
Cara memakai Claude Code sebelum commit: diff scope, build, URL publik, link Gumroad, CTA konsultasi, missing test, dan file tidak terkait.