Skip to main content
BlogWeb Dev

Optimisasi Gambar di Next.js: WebP, AVIF, dan Pengaturan yang Benar-benar Saya Kirim

Panduan praktis optimisasi gambar Next.js — format, konfigurasi, dan hasil dunia nyata dari mengoptimalkan matthewswong.com.

Matthews Wong

Software Engineer

Juni 202510 menit baca
Web Dev

Gambar biasanya 40-80% dari total berat byte halaman web. Sebelum saya mengoptimalkan gambar matthewswong.com, bagian hero menyajikan JPEG 1,4MB di ponsel. Setelah mengkonversi ke AVIF dengan optimizer bawaan Next.js, turun menjadi 180KB — pengurangan 87% tanpa kehilangan kualitas yang terlihat. Perubahan tunggal itu memindahkan LCP saya dari 3,1d menjadi 1,8d di ponsel.

WebP dan AVIF: Pertarungan Format

WebP memberikan penghematan ukuran file 25-40% dibandingkan JPEG dengan kualitas setara, dengan dukungan browser hampir universal (96%+ pada 2025). AVIF lebih jauh — penghematan 45-65% dibandingkan JPEG — tetapi dengan dukungan browser yang sedikit lebih rendah (90%+ pada 2025). Next.js menangani keputusan format secara otomatis: memeriksa header Accept dari browser, menyajikan AVIF jika didukung, fallback ke WebP, lalu fallback ke format asli.

Bagaimana Optimisasi Gambar Next.js Bekerja

Komponen next/image mencegat permintaan gambar dan menjalankannya melalui pipeline optimisasi gambar Next.js. Pada permintaan pertama, Next.js mengubah ukuran gambar, mengkonversinya ke format optimal untuk browser yang meminta, dan menyimpan hasilnya di disk. Permintaan berikutnya untuk gambar yang sama dengan ukuran yang sama mengenai cache dan kembali seketika.

Prop priority dan LCP

Perubahan yang paling berdampak untuk LCP adalah menambahkan prop `priority` ke gambar hero Anda. Tanpanya, Next.js menambahkan Intersection Observer untuk lazy-load gambar — artinya tidak mulai mengunduh sampai gambar masuk viewport. Untuk gambar hero yang terlihat pada pemuatan pertama, ini sangat buruk untuk LCP. Prop `priority` mengeluarkan `fetchpriority='high'` pada tag img dan menghapus perilaku lazy-loading. Jangan pernah lazy-load gambar LCP Anda.

Format Comparison (same image, equivalent quality):
─────────────────────────────────────────────────────
JPEG (original)     1,400 KB    Baseline
WebP                  860 KB    -39% vs JPEG
AVIF                  180 KB    -87% vs JPEG ✓ Best

Browser Support (2025):
  AVIF: Chrome 85+, Firefox 93+, Safari 16+  →  ~90% global
  WebP: Chrome 32+, Firefox 65+, Safari 14+  →  ~96% global

Next.js format selection (Accept header):
  Browser sends: Accept: image/avif,image/webp,*/*
  Next.js serves: AVIF → WebP → original
                  (most compressed format the browser accepts)

next.config.ts:
  images: {
    formats: ['image/avif', 'image/webp'],  // AVIF preferred
    deviceSizes: [640, 750, 828, 1080, 1200, 1920],
    minimumCacheTTL: 2592000, // 30 days
  }

Dari pengalaman saya mengoptimalkan matthewswong.com: gunakan `placeholder='blur'` dengan `blurDataURL` pada semua gambar hero dan di atas fold. Anda dapat menghasilkan blurDataURL secara otomatis dengan paket `plaiceholder` pada waktu build. Ini mengisi ruang dengan pratinjau beresolusi rendah yang blur sementara gambar penuh dimuat, menghilangkan CLS dari pemuatan gambar dan meningkatkan performa yang dirasakan secara signifikan.

Pengaturan next.config.ts yang Saya Kirim

Setelah iterasi melalui beberapa proyek, saya telah mendarat pada next.config.ts produksi yang mencakup tombol optimisasi gambar utama. Pengaturan kunci adalah: format (AVIF pertama, lalu WebP), deviceSizes (mencocokkan breakpoint umum), imageSizes (untuk thumbnail yang lebih kecil), dan minimumCacheTTL (diatur ke 30 hari untuk aset statis).

Gambar Jarak Jauh dan Keamanan

Jika Anda memuat gambar dari URL eksternal, Anda perlu mengkonfigurasi remotePatterns di next.config.ts. Ini adalah fitur keamanan — tanpanya, seseorang dapat membuat permintaan yang membuat server Anda mengunduh dan mengoptimalkan URL sembarang. Konfigurasikan hostname dan pola path opsional untuk setiap sumber tepercaya.

// next.config.ts — production image optimization config
import type { NextConfig } from "next"

const nextConfig: NextConfig = {
  images: {
    formats: ["image/avif", "image/webp"],
    deviceSizes: [640, 750, 828, 1080, 1200, 1920],
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
    minimumCacheTTL: 60 * 60 * 24 * 30, // 30 days
    remotePatterns: [
      {
        protocol: "https",
        hostname: "assets.contentful.com", // your CMS
      },
    ],
  },
}
export default nextConfig

// Hero component — LCP-critical image
import Image from "next/image"
import { getPlaiceholder } from "plaiceholder" // npm i plaiceholder

// At build time, generate blur placeholder:
const { base64 } = await getPlaiceholder(heroImageBuffer)

export function HeroSection() {
  return (
    <Image
      src="/hero.jpg"
      alt="Portfolio hero — Matthews Wong"
      width={1200}
      height={630}
      priority              // fetchpriority="high" — never lazy-load LCP
      placeholder="blur"
      blurDataURL={base64}  // low-res preview while loading
      sizes="100vw"         // hero spans full viewport width
    />
  )
}

// Below-fold blog card image — lazy-load correctly
export function BlogCard({ image, title }: { image: string; title: string }) {
  return (
    <Image
      src={image}
      alt={title}
      width={600}
      height={315}
      // No priority — lazy-load is correct for below-fold images
      sizes="(min-width: 768px) 600px, 100vw"
    />
  )
}

Gambar Responsif dengan sizes

Prop `sizes` pada next/image memberi tahu browser seberapa lebar gambar akan berada pada lebar viewport yang berbeda. Tanpa prop `sizes` yang akurat, browser default ke 100vw — artinya mengunduh gambar selebar viewport penuh bahkan untuk thumbnail 300px. Mendapatkan ini dengan benar dapat mengurangi payload gambar 30-50% untuk gambar di bawah fold.

Komponen Image Next.js tidak mengoptimalkan file SVG atau GIF — menyajikannya apa adanya. Untuk GIF animasi, ini adalah masalah: GIF animasi terkenal besar. Jika Anda menyajikan konten animasi, konversikan ke format video WebM atau AVIF dan gunakan elemen video. GIF 5MB dapat menjadi WebM 300KB.

Optimisasi Waktu Build vs Runtime

Pendekatan default Next.js adalah optimisasi runtime — gambar diproses pada permintaan pertama. Ini baik untuk sebagian besar kasus, tetapi untuk ekspor statis (`output: 'export'`) Next.js tidak dapat melakukan optimisasi runtime karena tidak ada server. Dalam kasus itu, Anda perlu pra-optimalkan gambar selama build menggunakan alat seperti sharp, imagemin, atau Squoosh.

Mengukur Dampaknya

Setelah mengaktifkan AVIF dan menambahkan priority ke gambar hero pada matthewswong.com, LCP lab meningkat dari 2,8d menjadi 1,4d. LCP lapangan meningkat dari 3,1d menjadi 1,9d selama 28 hari berikutnya. Total payload gambar di halaman beranda turun dari 1,8MB menjadi 340KB. Pelajarannya: optimisasi gambar adalah pekerjaan performa ROI tertinggi yang dapat Anda lakukan di sebagian besar situs berbasis konten.

Sumber & Bacaan Lebih Lanjut

FAQ

Pertanyaan Umum

Jawaban singkat untuk pertanyaan yang paling sering diajukan pembaca tentang topik ini.

Mengapa sebaiknya menggunakan AVIF daripada WebP untuk gambar di Next.js?

AVIF memberikan penghematan ukuran file 45-65% dibandingkan JPEG, lebih unggul dari WebP yang hanya 25-40%, tanpa kehilangan kualitas yang terlihat. Per 2025, AVIF didukung oleh 90%+ browser termasuk Chrome, Firefox, dan Safari. Next.js secara otomatis menyajikan AVIF ke browser yang mendukungnya dan fallback ke WebP atau format asli untuk yang lain, sehingga Anda mendapatkan format terbaik tanpa deteksi browser manual.

Apa yang sebenarnya dilakukan prop `priority` pada next/image, dan mengapa penting untuk LCP?

Tanpa `priority`, Next.js menambahkan Intersection Observer untuk lazy-load gambar sehingga browser tidak mulai mengunduhnya sampai gambar masuk viewport — sangat buruk untuk gambar hero yang terlihat saat pertama kali dimuat. Menambahkan `priority` mengeluarkan `fetchpriority='high'` pada tag img dan menghapus perilaku lazy-loading, sehingga browser bisa mengambil gambar segera saat HTML di-parse. Di matthewswong.com, perubahan ini saja memindahkan LCP dari 3,1d menjadi 1,8d di ponsel.

Bagaimana prop `sizes` memengaruhi payload gambar, dan apa yang terjadi jika saya mengabaikannya?

Tanpa prop `sizes` yang akurat, browser default ke 100vw dan mengunduh gambar selebar viewport penuh bahkan untuk thumbnail kecil. Misalnya, gambar kartu blog yang lebarnya 300px di desktop tapi lebar penuh di ponsel seharusnya menggunakan `(min-width: 768px) 300px, 100vw`. Mengatur ini dengan benar dapat mengurangi payload gambar 30-50% untuk gambar di bawah fold.

Apakah Next.js mengoptimalkan file SVG dan GIF melalui next/image?

Tidak — komponen Image Next.js tidak mengoptimalkan file SVG atau GIF dan menyajikannya apa adanya. Untuk GIF animasi khususnya, ini bermasalah karena ukurannya bisa sangat besar. Pendekatan yang disarankan adalah mengonversi konten animasi ke format video WebM atau AVIF dan menggunakan elemen video; GIF animasi 5MB bisa menyusut menjadi WebM 300KB.

Apa itu `placeholder='blur'` dan kapan sebaiknya digunakan?

Mengatur `placeholder='blur'` dengan `blurDataURL` mengisi ruang gambar dengan pratinjau beresolusi rendah yang blur sementara gambar penuh dimuat, menghilangkan Cumulative Layout Shift (CLS) dan meningkatkan performa yang dirasakan — terutama di koneksi lambat di mana gambar penuh bisa membutuhkan 1-2 detik. `blurDataURL` dapat dibuat secara otomatis saat build menggunakan paket `plaiceholder`, dan paling berguna untuk gambar hero dan di atas fold.