GioJSdocs
On this page

Image Optimization

Automatic AVIF/WebP conversion and resizing - no sharp, no CDN.

Use GioImage. It points at the Rust /_gio/image endpoint, which converts and resizes on demand through an AVIF → WebP → JPEG pipeline with a two-layer cache.

tsx
import { GioImage } from '@gio.js/react';

// public/hero.png - served at /hero.png (and /public/hero.png)
<GioImage src="/hero.png" alt="" width={1200} height={630} />

Widths and quality come from gio.toml

The optimizer only resizes to the widths listed in [images] allowed_widths (any other width is a 400), so GioImage builds its srcset from exactly that list and uses quality as the default. The server hands both to the renderer, and the page carries them to the browser, so the hydrated srcset is identical to the server's.

toml
[images]
allowed_widths = [640, 828, 1080, 1200, 1920]
quality        = 80
  • Fixed size (no sizes): a 1x and a 2x candidate, each the smallest allowed width that covers it (with the config above, width={500} → 640 and 1080).
  • sizes (or fill, which defaults it to 100vw): every allowed width as a w descriptor; the browser picks by layout width.
  • quality overrides the default per image (clamped to 1-100).
tsx
<GioImage src="/hero.png" alt="" width={1200} height={630}
  sizes="(max-width: 768px) 100vw, 50vw" />

Output formats

The optimizer serves the first format in [images] formats that the browser's Accept header names, and JPEG otherwise. AVIF is the smallest but several times slower to encode than WebP; leave it out to spend less CPU on first requests. The f= query parameter cannot pick a format the list leaves out (JPEG and PNG are always available).

toml
[images]
formats = ["webp"]          # default ["avif", "webp"]; [] = always JPEG

Above the fold: priority

priority loads the image eagerly with fetchpriority="high" and preloads it: the server render adds a <link rel="preload" as="image"> (with the same imagesrcset/imagesizes) to the document head. Use it for the LCP image; everything else lazy-loads.

tsx
<GioImage src="/hero.png" alt="" width={1200} height={630} priority />

Plain src

unoptimized skips the optimizer and renders src as-is. SVGs, data: and blob: URLs always do - there is nothing to resize. In a static export every image renders its plain src: a static host has no /_gio/image, so ship pre-sized files. So does every image when gio.toml turns the optimizer off with [images] enabled = false - for an app behind an image CDN, or one that wants no CPU-heavy endpoint. /_gio/image then answers 404.

tsx
<GioImage src="/avatar.gif" alt="" width={64} height={64} unoptimized />

Remote images

Allow remote sources explicitly in gio.toml with remote_patterns - anything not on the allowlist is rejected.

toml
[[images.remote_patterns]]
protocol = "https"
hostname = "images.example.com"
pathname = "/uploads/*"

Limits

The optimizer bounds what one request can cost. Each limit is an [images] key, and 0 lifts it (the server warns at startup when one is lifted). Remote fetches never follow redirects, and a src outside public/ or the allowlist is refused, whatever the limits.

toml
[images]
max_remote_bytes = 20971520     # largest remote source downloaded (20 MiB)
remote_timeout_secs = 30        # deadline for a whole remote download
max_source_dimension = 10000    # widest or tallest source decoded, in pixels
max_decode_bytes = 268435456    # decoder memory per source (256 MiB)