Menu
📱 Lihat versi lengkap (non-AMP)
Hugo Performance WebP

Cara Setup Image Optimization di Hugo dengan WebP dan Responsive Images

Editor: Hendra WIjaya
Update: 3 February 2026
Baca: 7 menit

Cara Setup Image Optimization di Hugo dengan WebP dan Responsive Images

Optimasi gambar adalah salah satu faktor terpenting untuk performa website. Hugo memiliki image processing built-in yang powerful untuk membuat gambar optimal secara otomatis.

Mengapa Image Optimization Penting?

Impact pada Performa

  • Page Size: Gambar sering 60-80% dari total page size
  • Loading Time: Gambar besar memperlambat LCP (Largest Contentful Paint)
  • Bandwidth: User dengan koneksi lambat akan mengalami delay signifikan
  • SEO: Google menggunakan Core Web Vitals sebagai ranking factor
  • UX: Website yang lambat meninggalkan pengunjung

Statistik Penting

Before Optimization:
- Total page size: 4.2MB
- Image size: 3.6MB (85%)
- LCP: 4.8 detik

After Optimization:
- Total page size: 1.1MB
- Image size: 0.4MB (36%)
- LCP: 1.2 detik

Hugo Image Processing Overview

Supported Formats

Hugo mendukung processing untuk format:

  • Input: JPG, PNG, GIF, WebP, SVG
  • Output: JPG, PNG, GIF, WebP (tergantung imagemagick)

Key Features

  1. Resizing: Resize gambar ke ukuran spesifik
  2. Format Conversion: Convert ke WebP untuk kompresi lebih baik
  3. Quality Control: Atur kompresi quality
  4. Filter: Lanczos, CatmullRom, MitchellNetravali
  5. Batch Processing: Process multiple images sekaligus

Setup Image Processing

1. Konfigurasi Hugo

File hugo.toml:

[imaging]
  # Default resample filter
  resampleFilter = "lanczos"
  
  # Default JPEG quality (0-100)
  quality = 80
  
  # Default hint about what type of image (photo/graphics)
  hint = "photo"
  
  # Default background color ( untuk PNG dengan transparency)
  bgColor = "#ffffff"

Resample Filters:

  • nearest: Fastest, lowest quality
  • box: Good for downscaling
  • linear: Linear interpolation
  • gaussian: Smooth
  • lanczos: Best quality (default, recommended)
  • catmullrom: Sharp
  • mitchellnetravali: Balanced

2. Store Images sebagai Page Resources

Struktur yang Benar (Page Bundles):

content/
└── posts/
    └── my-post/
        ├── index.md          # Content file
        ├── featured.jpg      # Page resource
        ├── screenshot-1.png  # Page resource
        └── diagram.svg       # Page resource

File content/posts/my-post/index.md:

---
title: "Judul Post"
date: 2026-02-03T10:00:00+07:00
image: featured.jpg
description: "Deskripsi post"
---

Content di sini...

Keuntungan Page Bundles:

  • Images dianggap sebagai page resources
  • Bisa di-process dengan Hugo Pipes
  • Asset co-location dengan content
  • Mudah untuk manage

3. Global Resources (Static Images)

Jika gambar digunakan di banyak halaman, simpan di assets/:

assets/
└── images/
    ├── logo.png
    ├── hero-bg.jpg
    └── icons/
        ├── facebook.svg
        └── twitter.svg

Implementasi di Templates

3.1 Basic Image Processing

Template untuk Page Resources:

{{ $image := .Resources.GetMatch .Params.image }}
{{ if $image }}
  {{ $resized := $image.Resize "800x" }}
  {{ $webp := $image.Resize "800x webp" }}
  
  <picture>
    <source srcset="{{ $webp.RelPermalink }}" type="image/webp">
    <img src="{{ $resized.RelPermalink }}" 
         alt="{{ .Title }}"
         width="{{ $resized.Width }}"
         height="{{ $resized.Height }}"
         loading="lazy">
  </picture>
{{ end }}

Template untuk Global Resources:

{{ $image := resources.Get "images/hero-bg.jpg" }}
{{ if $image }}
  {{ $resized := $image.Resize "1920x webp q80" }}
  
  <div style="background-image: url('{{ $resized.RelPermalink }}');
              background-size: cover;
              background-position: center;">
  </div>
{{ end }}

3.2 Responsive Images dengan Srcset

Implementasi responsive images untuk berbagai ukuran layar:

{{ $image := .Resources.GetMatch .Params.image }}
{{ if $image }}
  {{ $small := $image.Resize "400x" }}
  {{ $medium := $image.Resize "800x" }}
  {{ $large := $image.Resize "1200x" }}
  
  {{ $smallWebp := $image.Resize "400x webp" }}
  {{ $mediumWebp := $image.Resize "800x webp" }}
  {{ $largeWebp := $image.Resize "1200x webp" }}
  
  <picture>
    <!-- WebP versions -->
    <source 
      srcset="{{ $smallWebp.RelPermalink }} 400w,
              {{ $mediumWebp.RelPermalink }} 800w,
              {{ $largeWebp.RelPermalink }} 1200w"
      sizes="(max-width: 400px) 400px,
             (max-width: 800px) 800px,
             1200px"
      type="image/webp">
    
    <!-- Fallback JPG/PNG -->
    <source 
      srcset="{{ $small.RelPermalink }} 400w,
              {{ $medium.RelPermalink }} 800w,
              {{ $large.RelPermalink }} 1200w"
      sizes="(max-width: 400px) 400px,
             (max-width: 800px) 800px,
             1200px">
    
    <img src="{{ $medium.RelPermalink }}" 
         alt="{{ .Title }}"
         width="{{ $medium.Width }}"
         height="{{ $medium.Height }}"
         loading="lazy"
         decoding="async">
  </picture>
{{ end }}

Breakpoints Umum:

  • sm: 640px
  • md: 768px
  • lg: 1024px
  • xl: 1280px
  • 2xl: 1536px

3.3 Partial Reusable untuk Images

Buat partial layouts/partials/responsive-image.html:

{{ $image := .image }}
{{ $alt := .alt | default "" }}
{{ $class := .class | default "" }}
{{ $loading := .loading | default "lazy" }}

{{ if $image }}
  {{ $small := $image.Resize "400x webp" }}
  {{ $medium := $image.Resize "800x webp" }}
  {{ $large := $image.Resize "1200x webp" }}
  
  <picture>
    <source 
      srcset="{{ $small.RelPermalink }} 400w,
              {{ $medium.RelPermalink }} 800w,
              {{ $large.RelPermalink }} 1200w"
      sizes="(max-width: 400px) 400px,
             (max-width: 800px) 800px,
             1200px"
      type="image/webp">
    
    <img src="{{ $medium.RelPermalink }}" 
         alt="{{ $alt }}"
         class="{{ $class }}"
         width="{{ $medium.Width }}"
         height="{{ $medium.Height }}"
         loading="{{ $loading }}"
         decoding="async">
  </picture>
{{ end }}

Penggunaan:

{{ $image := .Resources.GetMatch .Params.image }}
{{ partial "responsive-image.html" (dict "image" $image "alt" .Title "class" "rounded-xl shadow-lg") }}

Advanced Image Processing

4.1 Custom Quality per Image

{{ $image := .Resources.GetMatch .Params.image }}
{{ if $image }}
  {{ $optimized := $image.Resize "800x webp q70" }}
  <img src="{{ $optimized.RelPermalink }}" alt="{{ .Title }}">
{{ end }}

Quality Guidelines:

  • q50: Kompresi tinggi, file kecil, quality acceptable untuk thumbnails
  • q70-q80: Balance optimal (recommended default)
  • q90-q95: High quality untuk hero images
  • q100: Lossless, hanya jika diperlukan

4.2 Image Filters

{{ $image := .Resources.GetMatch .Params.image }}
{{ if $image }}
  {{ $grayscale := $image.Filter (images.Grayscale) }}
  {{ $blur := $image.Filter (images.GaussianBlur 6) }}
  {{ $contrast := $image.Filter (images.Contrast 20) }}
  {{ $brightness := $image.Filter (images.Brightness 10) }}
  {{ $saturation := $image.Filter (images.Saturation 50) }}
  {{ $sepia := $image.Filter (images.Sepia 50) }}
  {{ $pixelate := $image.Filter (images.Pixelate 8) }}
  
  <!-- Kombinasi filters -->
  {{ $filtered := $image.Filter (images.Grayscale) (images.Contrast 30) }}
{{ end }}

4.3 Crop dan Fill

{{ $image := .Resources.GetMatch .Params.image }}
{{ if $image }}
  {{ $cropped := $image.Crop "600x400 center" }}
  {{ $filled := $image.Fill "600x400 center" }}
  {{ $fitted := $image.Fit "600x400" }}
  
  <img src="{{ $cropped.RelPermalink }}" alt="Cropped image">
{{ end }}

Options:

  • Smart: Hugo akan mendeteksi area penting
  • Center: Crop dari tengah
  • TopLeft, TopRight, BottomLeft, BottomRight

4.4 EXIF Data dan Rotasi

{{ $image := .Resources.GetMatch .Params.image }}
{{ if $image }}
  {{ $exif := $image.Exif }}
  
  {{ if $exif.Tags.Orientation }}
    {{ $rotated := $image.Filter (images.Rotate $exif.Tags.Orientation) }}
  {{ end }}
{{ end }}

Lazy Loading Implementation

5.1 Native Lazy Loading

<!-- Modern browsers support loading="lazy" -->
<img src="image.webp" alt="Deskripsi" loading="lazy" decoding="async">

<!-- Eager loading untuk hero image -->
<img src="hero.webp" alt="Hero" loading="eager" fetchpriority="high">

5.2 Intersection Observer (Fallback)

// assets/js/lazy-loading.js
document.addEventListener('DOMContentLoaded', () => {
  const imageObserver = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const img = entry.target;
        img.src = img.dataset.src;
        img.removeAttribute('data-src');
        observer.unobserve(img);
      }
    });
  }, {
    rootMargin: '50px 0px',
    threshold: 0.01
  });

  document.querySelectorAll('img[data-src]').forEach(img => {
    imageObserver.observe(img);
  });
});

5.3 Blur-up Placeholder (LQIP)

{{ $image := .Resources.GetMatch .Params.image }}
{{ if $image }}
  {{ $tiny := $image.Resize "20x q20" }}
  {{ $full := $image.Resize "800x webp" }}
  
  <div class="blur-up-container">
    <img src="{{ $tiny.RelPermalink }}" 
         class="blur-up-placeholder"
         style="filter: blur(10px); transition: opacity 0.3s;"
         aria-hidden="true">
    <img src="{{ $full.RelPermalink }}" 
         alt="{{ .Title }}"
         class="blur-up-image"
         style="opacity: 0; transition: opacity 0.3s;"
         onload="this.style.opacity=1; this.previousElementSibling.style.opacity=0;">
  </div>
{{ end }}

CSS untuk Image Optimization

6.1 Prevent Layout Shift (CLS)

/* Set aspect ratio untuk prevent CLS */
.image-container {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  background-color: #f3f4f6;
}

.image-container img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Atau dengan padding trick untuk older browsers */
.image-container-legacy {
  position: relative;
  width: 100%;
  padding-top: 56.25%; /* 16:9 aspect ratio */
}

.image-container-legacy img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

6.2 Art Direction (Picture Element)

<picture>
  <!-- Desktop -->
  <source media="(min-width: 1024px)" 
          srcset="large.webp 1200w"
          type="image/webp">
  
  <!-- Tablet -->
  <source media="(min-width: 768px)" 
          srcset="medium.webp 800w"
          type="image/webp">
  
  <!-- Mobile -->
  <source srcset="small.webp 400w"
          type="image/webp">
  
  <img src="fallback.jpg" alt="Description">
</picture>

Performance Monitoring

7.1 Lighthouse CI Integration

# .github/workflows/lighthouse.yml
name: Lighthouse CI
on: [push]
jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '18'
      - run: npm install -g @lhci/cli
      - run: hugo --minify
      - run: lhci autorun

File lighthouserc.json:

{
  "ci": {
    "collect": {
      "staticDistDir": "./public"
    },
    "assert": {
      "assertions": {
        "categories:performance": ["error", {"minScore": 0.9}],
        "categories:accessibility": ["error", {"minScore": 0.9}],
        "first-contentful-paint": ["error", {"maxNumericValue": 2000}],
        "largest-contentful-paint": ["error", {"maxNumericValue": 2500}]
      }
    }
  }
}

7.2 Image Size Budget

File budget.json:

[
  {
    "path": "/*",
    "resourceSizes": [
      {
        "resourceType": "image",
        "budget": 500000
      }
    ]
  }
]

CDN Integration

8.1 Cloudflare Image Optimization

# hugo.toml
[imaging]
  quality = 85
  resampleFilter = "lanczos"
<!-- Dengan Cloudflare Polish -->
<img src="/cdn-cgi/image/quality=80,format=auto/https://example.com/image.jpg" 
     alt="Description">

8.2 Cloudinary Integration

{{ $image := .Resources.GetMatch .Params.image }}
{{ if $image }}
  {{ $cloudinaryUrl := printf "https://res.cloudinary.com/your-cloud/image/upload/q_auto,f_auto,w_800/%s" $image.Name }}
  <img src="{{ $cloudinaryUrl }}" alt="{{ .Title }}">
{{ end }}

Troubleshooting

Issue: “image not found”

Solusi:

# Pastikan image di page bundle
ls content/posts/my-post/

# Atau gunakan resources.Get dengan path yang benar
{{ $image := resources.Get "images/photo.jpg" }}

Issue: “error processing image”

Solusi:

# Pastikan Hugo Extended terinstall
# Check dengan:
hugo version  # Harus ada kata "extended"

# Install Hugo Extended:
# macOS
brew install hugo

# Windows
choco install hugo-extended

Issue: “slow build with many images”

Solusi:

# Gunakan caching di hugo.toml
[imaging]
  resampleFilter = "box"  # Faster filter untuk development
  
# Atau process images secara incremental

Checklist Image Optimization

  • Images dalam page bundles atau assets folder
  • WebP format untuk modern browsers
  • JPG fallback untuk older browsers
  • Responsive srcset untuk berbagai ukuran layar
  • Width dan height attributes untuk prevent CLS
  • Lazy loading untuk images below fold
  • Eager loading untuk hero/featured images
  • Alt text yang deskriptif untuk accessibility
  • Quality optimization (q70-80 untuk balance)
  • Image processing di Hugo Pipes
  • CDN untuk global delivery
  • Preload untuk critical images

Kesimpulan

Hugo image processing memberikan:

Otomatis: Process saat build, tidak perlu manual work
Optimal: WebP format, responsive images, lazy loading
Fast: Pipeline processing, caching support
Flexible: Custom filters, cropping, quality control
SEO-friendly: Structured data, alt tags, performance

Dengan setup yang benar, Anda bisa mengurangi image size 60-80% tanpa quality loss yang signifikan.

Artikel Terkait

Bagikan:

Link Postingan: https://www.tirinfo.com/cara-setup-image-optimization-hugo/