Lewati ke konten
Kembali ke Blog

Cara Setup Image Optimization di Hugo dengan WebP dan Responsive Images

· · 10 menit baca

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">

&lt;!-- Fallback JPG/PNG --&gt;
&lt;source 
  srcset=&quot;{{ $small.RelPermalink }} 400w,
          {{ $medium.RelPermalink }} 800w,
          {{ $large.RelPermalink }} 1200w&quot;
  sizes=&quot;(max-width: 400px) 400px,
         (max-width: 800px) 800px,
         1200px&quot;&gt;

&lt;img src=&quot;{{ $medium.RelPermalink }}&quot; 
     alt=&quot;{{ .Title }}&quot;
     width=&quot;{{ $medium.Width }}&quot;
     height=&quot;{{ $medium.Height }}&quot;
     loading=&quot;lazy&quot;
     decoding=&quot;async&quot;&gt;

</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">

&lt;img src=&quot;{{ $medium.RelPermalink }}&quot; 
     alt=&quot;{{ $alt }}&quot;
     class=&quot;{{ $class }}&quot;
     width=&quot;{{ $medium.Width }}&quot;
     height=&quot;{{ $medium.Height }}&quot;
     loading=&quot;{{ $loading }}&quot;
     decoding=&quot;async&quot;&gt;

</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.

Ditulis oleh

Hendra Wijaya

Hanya hamba Allah Ta'ala yang berusaha berbuat baik..

Tinggalkan Komentar

Email tidak akan ditampilkan.

Facebook X / Twitter WhatsApp