Image optimization

Drop a full-size image into src/ or public/, point an <img> at it, and sitelo resizes it, converts it to a modern format, and rewrites the tag with a srcset. Nothing to import, no component to learn.

Enable it

export default {
  images: true,
}

Encoding is done by sharp, an optional peer dependency — install it alongside sitelo when you turn images on:

npm install -D sharp

Sites that skip image optimization never need to install it.

Write a plain <img>

import { html, body, img } from 'javascript-to-html'

export default () =>
  html({ lang: 'en' },
    body(
      img({ src: '/images/hero.png', alt: 'Sunrise over the harbour' }),
    ),
  )

A 3000×2000 source comes out the other side like this:

<img src="/assets/img/hero.a1b2c3d4-1200.webp"
     alt="Sunrise over the harbour"
     sizes="(max-width: 1200px) 100vw, 1200px"
     width="1200" height="800"
     loading="lazy" decoding="async"
     srcset="/assets/img/hero.9f8e7d6c-400.webp 400w,
             /assets/img/hero.5b4a3c2d-800.webp 800w,
             /assets/img/hero.a1b2c3d4-1200.webp 1200w">

What you get

The rewrite runs over the built HTML, so it covers images from src/ and public/ alike.

Options

export default {
  images: {
    widths: [400, 800, 1200],
    formats: ['avif', 'webp'],
    quality: { avif: 55, webp: 78, jpeg: 82 },
    exclude: ['**/og/**'],
  },
}

Two formats give you

AVIF is smaller but younger than WebP, so listing both lets the browser pick and keeps a fallback for old ones:

<picture>
  <source type="image/avif" srcset="/assets/img/hero.*-400.avif 400w, ..." sizes="...">
  <source type="image/webp" srcset="/assets/img/hero.*-400.webp 400w, ..." sizes="...">
  <img src="/assets/img/hero.*-1200.png" alt="..." srcset="..." width="1200" height="800">
</picture>

Opting out

Tags that already have a srcset, sit inside a <picture>, or point at an SVG, an animated GIF, or a remote URL are left untouched. For anything else, say so:

<img src="/images/exact.png" alt="Pixel art" data-no-optimize>

Social-card and favicon images live in <meta> and <link>, which this never rewrites — they keep their fixed URL.

Remote images

Content imported from a CMS often points at someone else’s server. Turn on remote and those images are downloaded, optimized, and served from your own domain:

export default {
  images: {
    remote: true,
    prune: true,
  },
}

A fetch that fails leaves the tag exactly as it was, with a warning — a flaky origin never fails your build. prune then removes local originals that nothing references any more.