Episode ini membahas komponen fungsional yang reusable, layouts dan nested layouts, template, shared layout untuk dynamic segment, loading UI dengan Suspense, serta styling memakai CSS modules, Tailwind CSS, dan styled-components.

Routing dari episode 4 menentukan struktur halaman; sekarang giliran mengisi halaman tersebut dengan UI yang rapi. Komponen adalah unit bangunan antarmuka, dan layout adalah cara Next.js membagikan struktur yang sama antar halaman.
Episode 5 membahas komponen fungsional yang reusable, nested layouts dan template, shared layout untuk segment dinamis, loading UI dengan file loading.tsx, serta tiga pendekatan styling populer: CSS modules, Tailwind CSS, dan styled-components.
Komponen reusable adalah fungsi yang menerima props dan mengembalikan JSX. Kuncinya adalah menghindari kode yang duplikat: jika sebuah tombol, kartu, atau badge dipakai di banyak tempat, ia pantas menjadi komponen sendiri. Contoh komponen badge sederhana:
type BadgeProps = {
label: string
variant?: "primary" | "neutral"
}
export default function Badge({ label, variant = "neutral" }: BadgeProps) {
return (
<span className={variant === "primary" ? "badge-primary" : "badge-neutral"}>
{label}
</span>
)
}Komponen Badge di atas dipakai ulang dengan prop label dan variant. Mengetik props dengan TypeScript membuat komponen aman dari kesalahan pemanggilan — salah nama prop langsung tertangkap oleh compiler.
Satu indikator komponen pantas dijadikan reusable: muncul di lebih dari dua tempat dengan struktur yang sama. Sebelum mengekstrak, biarkan polanya muncul dulu — ekstraksi prematur justru menambah abstraksi yang sulit dipahami dan dirawat.
Simpan komponen yang dipakai lintas halaman di folder components/. Untuk komponen yang hanya dipakai satu route, simpan dekat dengan route-nya, misalnya app/blog/[slug]/comments.tsx. Pola ini menjaga kedekatan antara komponen dan halaman yang memakainya, dan akan kita bahas lebih dalam di episode 18.
Konsistensi penamaan juga penting: gunakan PascalCase untuk file komponen dan lowerCamelCase untuk helper. Konvensi yang konsisten membuat tim menebak lokasi file dengan tepat.
Layout bisa bersarang. Layout akar app/layout.tsx membungkus seluruh aplikasi, lalu layout segment membungkus subset halaman. Buka halaman mana pun, layout akar selalu dipertahankan — hanya bagian children yang berubah. Ini membuat navbar global dan footer tidak perlu di-render ulang saat berpindah halaman.
Kadang kalian butuh kerangka yang baru setiap navigasi, misalnya untuk mengatur ulang state scroll atau menambahkan animasi masuk. Itulah gunanya template: tulis app/blog/template.tsx dengan struktur yang sama seperti layout, tapi komponen di dalamnya di-mount ulang setiap kali route berubah. Bedanya dengan layout: layout tetap (persistent), template dibuat baru.
Layout juga bisa dipakai bersama di seluruh nilai dynamic segment. Layout app/blog/layout.tsx membungkus halaman /blog, /blog/pengenalan-react, dan semua artikel lain — karena layout di file terluar segment berlaku untuk semua turunannya. Header berisi tautan kembali ke daftar blog bisa diletakkan di sini agar selalu tampil.
App Router mendukung streaming: buat file app/blog/loading.tsx dan Next.js menampilkannya sebagai placeholder sementara data halaman diproses di server. Dengan Suspense bawaan, loading state tidak perlu dikelola manual di tiap komponen:
export default function BlogLoading() {
return (
<div className="grid gap-4">
<div className="h-8 w-64 animate-pulse bg-gray-200" />
<div className="h-4 w-full animate-pulse bg-gray-200" />
<div className="h-4 w-full animate-pulse bg-gray-200" />
</div>
)
}Skeleton di atas ditampilkan otomatis saat halaman blog sedang menunggu data. Teknik streaming penuh dengan Suspense dijelaskan lebih lanjut di episode 23.
CSS modules menempelkan scope pada kelas sehingga nama tidak bentrok antar file. Buat file components/badge.module.css lalu import:
import styles from "./badge.module.css"
export default function Badge({ label }) {
return <span className={styles.primary}>{label}</span>
}styles.primary menghasilkan nama kelas unik seperti badge_primary__3xK2b, jadi kelas dengan nama sama di file lain tidak akan saling menimpa.
Tailwind CSS memakai kelas utility langsung di JSX — scaffold create-next-app sudah menyertakannya. Keunggulannya: tidak perlu menulis file CSS terpisah dan ukuran CSS final otomatis dipangkas saat build. Sementara styled-components memakai tagged template literal untuk menulis gaya dalam JavaScript, cocok untuk tema dinamis berbasis prop. Pilihan antar keduanya bergantung pada kebutuhan project: Tailwind untuk kecepatan dan konsistensi, styled-components untuk styling kondisional yang ekspresif.
Dalam praktik, banyak project menggabungkan pendekatan: Tailwind untuk styling cepat di seluruh halaman, CSS modules untuk komponen yang butuh gaya khusus, dan CSS variables untuk tema global. Tidak ada yang salah memakai lebih dari satu, selama tim konsisten dengan pembagian perannya.
Inti yang harus dibawa pulang:
Di episode 6 selanjutnya kita akan membahas data fetching dan API routes — pengambilan data server-side di App Router, static generation dengan generateStaticParams, Incremental Static Regeneration dengan revalidation, dan pembuatan backend API lewat route handler. Ini adalah jantung aplikasi fullstack kalian.