Lompat ke konten
dokumentasi.id
Commit 9a47d33
v2
Star3,4 rb

Kamu membaca versi lama. Halaman ini hanya bisa dibaca.

7.3

Komponen Gambar dan Skeleton

Direktori src/components/ menyediakan dua komponen yang menangani gambar yang belum selesai dimuat. NextImage membungkus next/image dan melacak status pemuatan lewat satu nilai state, sedangkan Skeleton menyediakan kotak placeholder dengan shimmer berbasis animasi CSS. Keduanya dipakai berdampingan di halaman pratinjau komponen (src/app/components/page.tsx:410-430); lihat Katalog Komponen Pratinjau untuk susunan halaman itu.

Status pemuatan di NextImage

NextImage menyimpan satu potongan state bernama status. Nilai awalnya bergantung pada prop useSkeleton: 'loading' bila useSkeleton bernilai true, dan 'complete' bila tidak. Inisialisasi itu hanya berjalan sekali, ketika komponen di-mount (src/components/NextImage.tsx:24-37).

stateDiagram-v2
    [*] --> loading : useSkeleton = true
    [*] --> complete : useSkeleton = false
    loading --> complete : onLoadingComplete
    complete --> [*]
    style loading fill:#f59e0b,color:#000
    style complete fill:#2d7d46,color:#fff
%% fig: Siklus hidup status pada NextImage
%% desc: Status dimulai sebagai loading hanya bila useSkeleton aktif, lalu berpindah ke complete saat callback onLoadingComplete dijalankan.
Fig. 1: Siklus hidup status pada NextImageStatus dimulai sebagai loading hanya bila useSkeleton aktif, lalu berpindah ke complete saat callback onLoadingComplete dijalankan.

Perpindahan dari loading ke complete dipicu oleh prop onLoadingComplete milik Image yang memanggil setStatus('complete') (src/components/NextImage.tsx:49-56). Di berkas ini tidak ada useEffect maupun pemanggilan setStatus('loading') lain, sehingga status tidak kembali ke loading ketika src berganti setelah gambar pertama selesai dimuat.

Selama status bernilai 'loading', className untuk Image dirakit dari classNames?.image yang digabung dengan cn, lalu ditambah animate-pulse beserta classNames?.blur (src/components/NextImage.tsx:44-48). Penggabungan kelas itu memakai util cn yang membungkus clsx dan twMerge (src/lib/utils.ts:5-7); rincian util tersebut ada di Utilitas, Logger, dan Environment.

Elemen terluar komponen ini adalah figure, bukan Image. figure menerima className dari pemanggil, dan hanya memasang style lebar bila className tidak memuat w- (src/components/NextImage.tsx:39-43). Pemeriksaan itu dilakukan dengan className?.includes('w-') (src/components/NextImage.tsx:24-37).

⚠️ includes('w-') adalah pencocokan substring, bukan pencocokan kelas utuh. Kelas apa pun yang memuat teks w- membuat komponen menganggap lebar sudah ditetapkan, sehingga figure tidak lagi mendapat style selebar ${width}px.

⚠️ JSDoc komponen mengingatkan agar useSkeleton tidak dipakai pada gambar transparan, karena latar animate-pulse akan terlihat di belakang gambar (src/components/NextImage.tsx:19-23).

Props NextImage

Tipe NextImageProps menggabungkan tiga hal: prop tambahan milik NextImage, satu union yang mewajibkan width dan height kecuali ketika layout: 'fill' dipakai, dan seluruh ImageProps dari next/image (src/components/NextImage.tsx:6-17).

PropTipeDefaultPerilaku
useSkeletonbooleanfalseMenentukan nilai awal status; true berarti mulai dari 'loading'
classNames.imagestring–Kelas dasar untuk Image, selalu ikut digabung lewat cn
classNames.blurstring–Kelas tambahan yang hanya aktif selama status 'loading'
altstringwajibDiteruskan langsung ke Image
width, heightstring | numberwajib kecuali layout: 'fill'Dipakai figure untuk lebar fallback ${width}px
layout'fill'–Membuat width dan height menjadi opsional
classNamestring–Ditaruh di figure; kehadiran w- mematikan lebar fallback
...restImageProps–Diteruskan ke Image setelah prop di atas

Skeleton dan animasi shimmer

Skeleton adalah div tipis yang meneruskan seluruh React.ComponentPropsWithoutRef<'div'>. Kelas dasarnya selalu animate-shimmer bg-[#f6f7f8], lalu digabung dengan className dari pemanggil lewat cn (src/components/Skeleton.tsx:5-20).

Karena tidak ada kelas latar gradien di className dasarnya, efek shimmer dibangun dari dua sumber: utilitas animate-shimmer dan style inline (src/components/Skeleton.tsx:5-20).

BagianNilai
Kelas dasaranimate-shimmer bg-[#f6f7f8]
backgroundImagelinear-gradient(to right, #f6f7f8 0%, #edeef1 20%, #f6f7f8 40%, #f6f7f8 100%)
backgroundSize700px 100%
backgroundRepeatno-repeat
Token animasi--animate-shimmer: shimmer 1.3s linear infinite
@keyframes shimmerbackground-position bergerak dari -700px 0 ke 700px 0

Nilai token dan keyframes itu didefinisikan di src/styles/globals.css dalam blok @theme (src/styles/globals.css:24-27) dan @keyframes shimmer (src/styles/globals.css:55-62). Lebar backgroundSize sama dengan jarak tempuh keyframes, yaitu 700px, sehingga gradien bergeser tepat satu lebar kotak per siklus. Sistem token warna dan tema selengkapnya dibahas di Styling, Token Warna, dan Tema.

graph LR
    kf["@keyframes shimmer: -700px 0 to 700px 0"] --> theme["--animate-shimmer: shimmer 1.3s linear infinite"]
    theme --> skel["Skeleton div with animate-shimmer"]
    skel --> inline["inline style: linear-gradient, backgroundSize 700px 100%"]
    caller["className from caller: h-72 w-72"] --> skel
    status["status === 'loading'"] --> pulse["animate-pulse and classNames.blur"]
    pulse --> img["Image from next/image"]
    style kf fill:#f59e0b,color:#000
    style theme fill:#f59e0b,color:#000
    style skel fill:#8b5cf6,color:#fff
    style inline fill:#8b5cf6,color:#fff
    style caller fill:#8b5cf6,color:#fff
    style status fill:#f59e0b,color:#000
    style pulse fill:#f59e0b,color:#000
    style img fill:#336791,color:#fff
%% fig: Sumber kelas animasi pada Skeleton dan NextImage
%% desc: Skeleton mengambil animasi dari token --animate-shimmer yang merujuk keyframes shimmer, sedangkan NextImage memakai animate-pulse selama status masih loading.
Fig. 2: Sumber kelas animasi pada Skeleton dan NextImageSkeleton mengambil animasi dari token --animate-shimmer yang merujuk keyframes shimmer, sedangkan NextImage memakai animate-pulse selama status masih loading.

Perhatikan bahwa keduanya memakai mekanisme berbeda: Skeleton bergantung pada token --animate-shimmer di globals.css, sementara NextImage memakai utilitas animate-pulse untuk efek denyut (src/components/NextImage.tsx:44-48).

Pemakaian di halaman pratinjau

Halaman src/app/components/page.tsx menampilkan keduanya di dalam satu daftar. NextImage dipanggil dengan useSkeleton, className='w-32 md:w-40', src favicon, serta width dan height bernilai string '180'. Karena className memuat w-, figure tidak mendapat lebar inline, dan lebar diatur oleh kelas responsif tersebut. Skeleton dipanggil dengan className='h-72 w-72' sehingga ukurannya ditentukan pemanggil (src/app/components/page.tsx:410-430).

⚠️ width yang dikirim sebagai string pada contoh itu tetap dipakai untuk atribut width milik Image, bukan untuk style figure. Bila className tidak memuat w-, nilai itulah yang menjadi style lebar fallback (src/components/NextImage.tsx:39-43).

Untuk komponen tombol dan tautan yang juga tampil di halaman pratinjau itu, lihat Komponen Tombol dan Komponen Link.