Kenapa ini penting
Gambar biasanya bagian terberat dari sebuah halaman. Satu foto produk 4 MB dari kamera HP bisa membuat halaman terasa lambat di koneksi 4G, dan tata letak yang "loncat" saat gambar selesai dimuat membuat user salah klik. Font dari Google Fonts juga bisa membuat teks berkedip atau tidak terbaca beberapa detik pertama. Next.js punya komponen next/image dan modul next/font yang mengurus masalah ini otomatis, plus konvensi file untuk ikon dan gambar share sosial media. Hasilnya langsung terasa di skor Core Web Vitals (ukuran kecepatan halaman dari Google) dan SEO.
Konsep inti
Folder public
File di folder public/ disajikan apa adanya dari akar URL. public/logo.png bisa diakses di /logo.png. Cocok untuk logo, file robots.txt statis, PDF, atau gambar yang dipakai via URL. Jangan taruh file rahasia di sini, karena semua orang bisa mengunduhnya. Nama file jangan sama dengan nama rute di app/.
next/image: <img> yang pintar
Komponen Image dari next/image memperluas <img> dengan:
- Ukuran otomatis: membuat beberapa versi gambar sesuai lebar layar dan mengirim format modern seperti WebP. HP tidak perlu mengunduh gambar selebar monitor.
- Stabilitas tata letak: ruang untuk gambar dipesan sejak awal sehingga tidak terjadi layout shift (CLS).
- Lazy loading: gambar di bawah layar baru dimuat saat hampir terlihat.
Analoginya seperti tukang jahit: alih-alih memberi semua orang baju ukuran XXL, ia menjahit sesuai ukuran badan masing-masing.
Props penting:
| Prop | Fungsi |
|---|---|
src |
Path lokal (/foto.jpg), hasil import, atau URL remote |
alt |
Teks alternatif, wajib untuk aksesibilitas dan SEO |
width, height |
Ukuran asli/rasio gambar (wajib untuk gambar remote, kecuali pakai fill) |
fill |
Gambar memenuhi elemen induk; induk harus position: relative (relative di Tailwind) |
sizes |
Petunjuk lebar gambar di layar, misalnya "(max-width: 768px) 100vw, 800px" |
placeholder="blur" |
Tampilkan versi buram dulu (otomatis untuk gambar yang di-import) |
preload |
Muat sangat awal lewat <link> di <head>, untuk gambar hero/LCP |
quality |
Kualitas kompresi; nilainya harus ada di images.qualities |
Perubahan di Next.js 16 yang perlu kamu tahu:
- Prop
prioritydeprecated, digantipreload. Dokumentasi juga menyebut bahwa dalam banyak kasusloading="eager"ataufetchPriority="high"lebih tepat daripadapreload. - Default
images.qualitiessekarang hanya[75]. Kalau kamu menulisquality={90}tanpa menambahkannya ke config, nilainya dibulatkan ke angka terdekat yang diizinkan. - Default
images.minimumCacheTTLnaik dari 60 detik menjadi 4 jam.
Gambar lokal vs remote
- Di-import (
import foto from './foto.jpg'): Next.js tahu ukurannya, jadiwidth/heighttidak perlu, danplaceholder="blur"langsung bisa. - String dari
public(src="/foto.jpg"): beriwidthdanheight, ataufill. - Remote (
src="https://..."): beriwidth/heightataufill, dan daftarkan domainnya diimages.remotePatternsdinext.config. Tanpa itu, Next.js menolak memproses gambar, mencegah server kamu dipakai orang lain sebagai pengolah gambar gratis.
next/font: font tanpa kedipan
next/font mengunduh font saat build dan menyajikannya dari domainmu sendiri. Browser tidak perlu menghubungi Google, lebih cepat, lebih menjaga privasi, dan tanpa layout shift karena ukuran fallback disesuaikan otomatis.
next/font/google:import { Inter } from 'next/font/google', panggil sebagai fungsi di level modul (bukan di dalam komponen).next/font/local: untuk file.woff2milikmu sendiri.- Font variabel (variable font) disarankan; untuk font non-variabel, sebutkan
weight. - Opsi
variable: '--font-...'membuat CSS variable. Inilah cara template Next.js menghubungkan font ke Tailwind v4: diglobals.cssada@theme inline { --font-sans: var(--font-geist-sans); }, sehingga kelasfont-sansmemakai Geist.
File metadata khusus di app/
Beberapa nama file di app/ otomatis diubah menjadi tag <head>:
| File | Hasil |
|---|---|
app/favicon.ico |
Ikon tab browser |
app/icon.png / icon.svg |
Ikon aplikasi |
app/apple-icon.png |
Ikon saat disimpan ke layar utama iPhone |
app/opengraph-image.jpg |
Gambar saat link dibagikan (WhatsApp, Facebook, LinkedIn) |
app/twitter-image.jpg |
Gambar khusus X/Twitter |
opengraph-image.jsx |
Gambar OG dibuat dengan kode memakai ImageResponse dari next/og |
File di subfolder lebih spesifik dan mengalahkan yang di atasnya: app/blog/opengraph-image.jpg khusus untuk /blog. ImageResponse memakai JSX dengan subset CSS (flexbox, bukan grid).
Contoh kode
Daftarkan domain gambar remote:
// next.config.mjs
const nextConfig = {
cacheComponents: true,
partialPrefetching: true,
images: {
remotePatterns: [
{ protocol: 'https', hostname: 'images.unsplash.com' },
],
qualities: [75, 90], // izinkan quality={90}
},
}
export default nextConfig
Tiga cara memakai Image:
// app/galeri/page.jsx
import Image from 'next/image'
import logo from '../../public/next.svg' // import statis → ukuran diketahui otomatis
export default function GaleriPage() {
return (
<main className="space-y-6 p-6">
{/* 1. Import statis: tanpa width/height */}
<Image src={logo} alt="Logo Next.js" />
{/* 2. Remote: wajib width/height + domain di remotePatterns */}
<Image
src="https://images.unsplash.com/photo-1509042239860-f550ce710b93"
alt="Secangkir kopi di meja kayu"
width={800}
height={533}
preload // gambar hero (pengganti 'priority')
sizes="(max-width: 768px) 100vw, 800px"
/>
{/* 3. fill: mengikuti ukuran induk yang relative */}
<div className="relative h-64 w-full">
<Image src="/vercel.svg" alt="Logo Vercel" fill className="object-contain" />
</div>
</main>
)
}
// → di HTML: <img srcset="/_next/image?url=...&w=640&q=75 640w, ..."> dengan format WebP
Font Google dan font lokal sekaligus, dihubungkan ke Tailwind:
// app/layout.jsx
import { Inter } from 'next/font/google'
import localFont from 'next/font/local'
import './globals.css'
const inter = Inter({ subsets: ['latin'], variable: '--font-inter' })
const judul = localFont({
src: './fonts/Judul-Bold.woff2', // path relatif terhadap file ini
variable: '--font-judul',
})
export default function RootLayout({ children }) {
return (
<html lang="id" className={`${inter.variable} ${judul.variable}`}>
<body className="font-sans">{children}</body>
</html>
)
}
/* app/globals.css */
@import "tailwindcss";
@theme inline {
--font-sans: var(--font-inter);
--font-judul: var(--font-judul);
}
/* → kelas font-sans memakai Inter, kelas font-judul memakai font lokal */
Gambar Open Graph yang dibuat dengan kode untuk setiap artikel:
// app/blog/[slug]/opengraph-image.jsx
import { ImageResponse } from 'next/og'
import { getPost, getPosts } from '@/lib/posts'
export const size = { width: 1200, height: 630 }
export const contentType = 'image/png'
export const alt = 'Gambar artikel Kopiku'
export async function generateStaticParams() {
const posts = await getPosts()
return posts.map((post) => ({ slug: post.slug }))
}
export default async function Image({ params }) {
const { slug } = await params
const post = await getPost(slug)
return new ImageResponse(
(
<div
style={{
width: '100%', height: '100%', display: 'flex', alignItems: 'center',
justifyContent: 'center', background: '#3b2414', color: 'white',
fontSize: 72, padding: 60,
}}
>
{post?.judul ?? 'Kopiku'}
</div>
),
size
)
}
// → /blog/halo-nextjs/opengraph-image → PNG 1200×630 bertuliskan "Halo, Next.js!"
// → tag <meta property="og:image"> ditambahkan otomatis ke halaman artikel
Kesalahan umum
- Gambar remote tanpa
remotePatterns. Muncul error bahwa hostname belum dikonfigurasi. Tambahkan domain dinext.config(sespesifik mungkin, bahkan denganpathname). filltanpa indukrelativedan tinggi. Gambar tidak tampil atau memenuhi seluruh halaman. Beri indukrelativedan ukuran (misalnyah-64).- Masih memakai
priority. Deprecated di Next.js 16. Ganti denganpreload, atauloading="eager"/fetchPriority="high". quality={90}tapi hasilnya tetap 75. Defaultimages.qualitieshanya[75]. Tambahkan nilai yang dibutuhkan ke config.- Memanggil fungsi font di dalam komponen.
Inter({...})harus di level modul agar font hanya dimuat sekali. Pakai<link>Google Fonts manual juga tidak perlu lagi.
Latihan
- Mudah, Taruh
kopi.jpgdipublic/, lalu tampilkan dengan<Image>berukuran 600×400 dan alt yang deskriptif. - Sedang, Buat kartu produk dengan gambar
filldi kotak rasio 4:3 (relative aspect-[4/3]) dansizesyang benar untuk grid 3 kolom di desktop dan 1 kolom di HP. - Menantang, Buat
app/opengraph-image.jsxuntuk seluruh situs berisi nama toko dan slogan di dua baris, latar cokelat, ukuran 1200×630.
Lihat solusi
1.
// app/page.jsx
import Image from 'next/image'
export default function HomePage() {
return (
<Image
src="/kopi.jpg"
alt="Biji kopi arabika Gayo di atas karung goni"
width={600}
height={400}
/>
)
}
2.
// components/KartuProduk.jsx
import Image from 'next/image'
export default function KartuProduk({ nama, gambar }) {
return (
<div className="overflow-hidden rounded-lg border">
<div className="relative aspect-[4/3]">
<Image
src={gambar}
alt={nama}
fill
className="object-cover"
sizes="(max-width: 768px) 100vw, 33vw" // HP: selebar layar; desktop: sepertiga
/>
</div>
<p className="p-3 font-semibold">{nama}</p>
</div>
)
}
3.
// app/opengraph-image.jsx
import { ImageResponse } from 'next/og'
export const size = { width: 1200, height: 630 }
export const contentType = 'image/png'
export const alt = 'Kopiku, kopi nusantara'
export default function Image() {
return new ImageResponse(
(
<div
style={{
width: '100%', height: '100%', display: 'flex', flexDirection: 'column',
alignItems: 'center', justifyContent: 'center',
background: '#4a2c17', color: '#fde68a',
}}
>
<div style={{ fontSize: 96, fontWeight: 700 }}>Kopiku</div>
<div style={{ fontSize: 40, marginTop: 20 }}>Kopi nusantara, diseduh sepenuh hati</div>
</div>
),
size
)
}
Ringkasan
- File di
public/disajikan dari akar URL; jangan taruh file rahasia di sana. next/imagemembuat ukuran responsif, format modern, lazy loading, dan mencegah layout shift;altwajib.- Gambar remote butuh
width/height(ataufill) dan domain diimages.remotePatterns. - Next.js 16:
prioritydigantipreload, defaultqualities[75], defaultminimumCacheTTL4 jam. next/fontmenyajikan font dari domainmu sendiri; hubungkan ke Tailwind v4 lewatvariabledan@theme. Filefavicon.ico,icon,opengraph-imagediapp/otomatis menjadi tag<head>.