Salin dan Bagikan
Cara Setup Image Optimization di Hugo dengan WebP dan Responsive Images - Panduan lengkap optimasi gambar di Hugo dengan WebP format, responsive images, lazy loading, dan …

Cara Setup Image Optimization di Hugo dengan WebP dan Responsive Images

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

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

Hendra WIjaya
Tirinfo
7 minutes.
3 February 2026