Optimasi Gambar WebP dan AVIF dengan Sharp di Node.js

Pengertian

Sharp adalah pustaka pemrosesan gambar untuk Node.js yang dibangun di atas libvips. Ia mengubah ukuran, memotong, memutar, dan mengompresi gambar jauh lebih cepat dan hemat memori dibanding pustaka berbasis ImageMagick, karena libvips memproses gambar secara streaming dan paralel.

Pada website statis, Sharp bermanfaat untuk:

  • menghasilkan format modern WebP dan AVIF yang jauh lebih kecil dari JPEG/PNG,
  • membuat beberapa ukuran gambar responsif,
  • menghasilkan gambar Open Graph otomatis (og.png),
  • mengoptimalkan ikon dan aset saat build.

Kelebihan

  • Cepat: sering 4-5x lebih cepat dari alternatif berbasis Node murni.
  • Hemat memori: penting saat build di CI dengan RAM terbatas.
  • Format lengkap: JPEG, PNG, WebP, AVIF, TIFF, GIF, SVG input.
  • API ringkas: method berantai (resize().webp().toFile()).
  • Binari siap pakai: instalasi npm langsung menyertakan binari platform umum.
  • Bisa jalan saat build: gambar optimal tanpa proses manual.

Kekurangan

  • Ketergantungan binari native: kompatibilitas bergantung platform (glibc vs musl, arsitektur CPU).
  • Blokir event loop: proses berat sebaiknya dijalankan di worker atau skrip build terpisah.
  • Tanpa efek grafis lanjutan: bukan pengganti Photoshop untuk manipulasi kompleks.
  • Kompleksitas sharpness/resize: kualitas perlu disesuaikan per kasus.
  • AVIF lebih lambat dikodekan dibanding WebP meski hasilnya lebih kecil.
Catatan penting: Sharp memasang binari sesuai platform saat npm install. Jika proyek dibangun di mesin berbeda dari tempat deploy (mis. Windows lokal lalu Vercel Linux), biarkan Sharp dipasang ulang di platform target — jangan meng-commit folder node_modules.

Instalasi

bash
npm install sharp

Untuk proyek yang hanya memakai Sharp saat build, jadikan dependency produksi bila dipakai di buildCommand Vercel:

bash
npm install sharp --save

Verifikasi cepat:

bash
node -e "const s=require('sharp'); console.log(s.versions)"

Mengubah Ukuran dan Konversi

javascript
const sharp = require('sharp');

async function optimize(input, output) {
  const info = await sharp(input)
    .resize({ width: 1200, withoutEnlargement: true })
    .webp({ quality: 78 })
    .toFile(output);

  console.log(`${input} -> ${output} (${Math.round(info.size / 1024)} KB)`);
}

optimize('foto.jpg', 'foto.webp');

Membuat versi AVIF sekaligus:

javascript
await sharp('foto.jpg')
  .resize({ width: 1200, withoutEnlargement: true })
  .avif({ quality: 50 })
  .toFile('foto.avif');

Optimasi Massal Satu Folder

javascript
const fs = require('fs');
const path = require('path');
const sharp = require('sharp');

const SRC = 'assets/img';
const OUT = 'public/img';

async function run() {
  fs.mkdirSync(OUT, { recursive: true });
  const files = fs.readdirSync(SRC).filter((f) => /\.(jpe?g|png)$/i.test(f));

  for (const f of files) {
    const base = path.parse(f).name;
    const input = path.join(SRC, f);

    await sharp(input)
      .resize({ width: 1200, withoutEnlargement: true })
      .webp({ quality: 78 })
      .toFile(path.join(OUT, `${base}.webp`));

    await sharp(input)
      .resize({ width: 1200, withoutEnlargement: true })
      .avif({ quality: 50 })
      .toFile(path.join(OUT, `${base}.avif`));

    console.log(`  + ${base}`);
  }
}

run().catch((e) => { console.error(e); process.exit(1); });

Thumbnail Persegi dengan Crop

javascript
await sharp('foto.jpg')
  .resize(320, 320, { fit: 'cover', position: 'attention' })
  .webp({ quality: 80 })
  .toFile('thumb.webp');

position: 'attention' memilih area paling menarik otomatis; gunakan 'center' bila ingin hasil yang dapat diprediksi.

Menghasilkan Gambar Open Graph Otomatis

Sharp dapat menggabungkan latar dan teks SVG, lalu mengekspor PNG 1200x630:

javascript
const sharp = require('sharp');

const svg = `
<svg width="1200" height="630" xmlns="http://www.w3.org/2000/svg">
  <rect width="1200" height="630" fill="#0d0d12"/>
  <text x="80" y="320" fill="#ffffff" font-size="72" font-family="sans-serif">
    Masyarakat
  </text>
  <text x="80" y="400" fill="#8b8b96" font-size="34" font-family="sans-serif">
    Mari Berbagi
  </text>
</svg>`;

sharp(Buffer.from(svg))
  .png()
  .toFile('og.png')
  .then((info) => console.log('og.png', info.width, info.height));

Tambahkan skrip ke package.json:

json
{
  "scripts": {
    "og": "node scripts/generate-og.js"
  }
}

Tabel Method Penting

MethodFungsi
resize({ width, height, fit })Ubah ukuran / crop
webp({ quality })Ekspor WebP
avif({ quality })Ekspor AVIF
jpeg({ quality, mozjpeg })Ekspor JPEG optimal
png({ compressionLevel })Ekspor PNG
rotate()Putar sesuai EXIF
metadata()Baca dimensi & format
toBuffer() / toFile()Keluaran buffer / berkas

Integrasi ke Build

json
{
  "scripts": {
    "images": "node scripts/optimize-images.js",
    "build": "npm run images && node scripts/build-articles.js"
  }
}

Menyisipkan langkah pengolahan gambar sebelum pre-render HTML memastikan aset sudah optimal saat diunggah.

Troubleshooting

  • Could not load the sharp module / binari tidak cocok: hapus node_modules dan package-lock.json, lalu npm ci di platform target. Jalankan npm rebuild sharp bila perlu.
  • Gagal saat build di CI Linux minimal: Sharp membutuhkan glibc; untuk Alpine (musl) gunakan varian yang sesuai atau basis node:20-bookworm.
  • Deploy Vercel gagal karena Sharp: pastikan Sharp ada di dependencies, bukan devDependencies, karena dipakai saat buildCommand.
  • Proses lambat: hindari memproses gambar besar berulang; cache hasil dengan memeriksa waktu modifikasi berkas.
  • Warna berubah pada foto: sertakan .rotate() untuk menghormati orientasi EXIF dan jangan menurunkan kualitas terlalu jauh.
  • SVG tidak ter-render: Sharp memakai librsvg; pastikan SVG valid tanpa referensi font eksternal.

Kesimpulan

Sharp menjadikan optimasi gambar sebagai bagian otomatis dari alur build: satu perintah menghasilkan WebP, AVIF, thumbnail, hingga og.png. Hasilnya, halaman statis memuat gambar lebih cepat tanpa sentuhan manual — asalkan binari dipasang di platform yang sama dengan tempat deploy.

Artikel Terkait