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.
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.
[images]
allowed_widths = [640, 828, 1080, 1200, 1920]
quality = 80- Fixed size (no
sizes): a1xand a2xcandidate, each the smallest allowed width that covers it (with the config above,width={500}→ 640 and 1080). sizes(orfill, which defaults it to100vw): every allowed width as awdescriptor; the browser picks by layout width.qualityoverrides the default per image (clamped to 1-100).
<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).
[images]
formats = ["webp"] # default ["avif", "webp"]; [] = always JPEGAbove 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.
<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.
<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.
[[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.
[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)