GioJSdocs
On this page

<GioImage>

An <img> served through the built-in image optimizer, with a srcset of the widths gio.toml allows, lazy loading, and a preload for the hero image.

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

export default function Home() {
  // public/hero.jpg, served at /hero.jpg
  return <GioImage src="/hero.jpg" alt="The harbor at dawn" width={1200} height={630} priority />;
}

The Rust server resizes and re-encodes the image on its first request at /_gio/image (AVIF or WebP when the browser accepts them) and caches the result. GioImage builds the URLs; how the optimizer works is in Image Optimization.

Reference

PropTypeDefaultDescription
src (required)string-A file in public/ (/hero.jpg or /public/hero.jpg), or a remote URL that [[images.remote_patterns]] allows.
alt (required)string-The text alternative. Use "" for a decorative image.
width (required)number-The rendered width in CSS pixels. Sets the width attribute and picks the 1x/2x candidates. Not used with fill.
height (required)number-The rendered height in CSS pixels, for the height attribute, so the browser reserves the space before the image loads. Not used with fill.
prioritybooleanfalseLoad eagerly with fetchpriority="high" and preload the image in the document head. For the largest image above the fold.
qualitynumber[images] quality (75)Encoder quality, rounded and clamped to 1-100.
sizesstring-How wide the image renders per breakpoint ("(max-width: 768px) 100vw, 50vw"). Switches the srcset to every allowed width.
fillbooleanfalseFill the parent: no width/height attributes, width: 100%; height: 100%; object-fit: cover, and sizes defaulting to 100vw.
placeholder'blur' | 'empty'-With 'blur' and a blurDataURL, show that image behind this one while it loads. 'empty' or no value shows nothing.
blurDataURLstring-The placeholder image, usually a tiny base64 data: URL. Used only with placeholder="blur".
unoptimizedbooleanfalseSkip the optimizer and render src as is.
classNamestring-Passed to the <img>.

GioImage takes only these props: id, style, onLoad, decoding and other <img> attributes are not forwarded. Style it through className.

How the srcset is built

Every URL is /_gio/image?src=<encoded src>&w=<width>&q=<quality>. The optimizer answers 400 for any width outside [images] allowed_widths, so the widths come from exactly that list - the server hands it to the renderer, and the page carries it to the browser, so the hydrated markup matches the server's.

  • Fixed size (no sizes, no fill): a 1x candidate at the smallest allowed width that covers width, and a 2x one covering twice that (left out when it is the same width). src is the 2x URL. A width above every allowed width uses the largest.
  • With sizes or fill: every allowed width as a w descriptor, src at the largest, and the sizes attribute (100vw when only fill is set). The browser picks by layout width and screen density.
  • An empty allowed_widths list, or a width of 0 without sizes, requests no width at all: one URL, converted but not resized.

With the default allowed_widths, <GioImage src="/hero.png" alt="Hero" width={400} height={300} /> renders (line breaks added):

text
<img src="/_gio/image?src=%2Fhero.png&amp;w=828&amp;q=75"
     srcSet="/_gio/image?src=%2Fhero.png&amp;w=640&amp;q=75 1x, /_gio/image?src=%2Fhero.png&amp;w=828&amp;q=75 2x"
     width="400" height="300" alt="Hero" loading="lazy"/>

Plain src

GioImage renders src untouched, with no srcset, when:

  • unoptimized is set;
  • the source is an SVG (a path ending in .svg, any case, before ? or #), a data: URL or a blob: URL;
  • the page comes from gio export - a static host has no optimizer;
  • gio.toml turns the optimizer off with [images] enabled = false (/_gio/image then answers 404).

placeholder and blurDataURL

placeholder="blur" with a blurDataURL sets that URL as the <img>'s CSS background (background-size: cover), so a blurred preview fills the box until the real image paints over it. GioJS does not generate the preview: produce a tiny image (10-20 pixels wide) when you store the original, and pass it as a base64 data: URL. Without a blurDataURL, placeholder="blur" does nothing.

priority

A priority image renders with loading="eager" and fetchpriority="high", and React's server renderer adds a <link rel="preload" as="image"> with the same imagesrcset and imagesizes to the head, so the browser starts the download before it reaches the <img>. Every other image is loading="lazy".

Examples

A responsive hero image

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

export default function Home() {
  return (
    <GioImage
      src="/hero.jpg"
      alt="The harbor at dawn"
      width={1600}
      height={900}
      sizes="(max-width: 768px) 100vw, 60vw"
      priority
    />
  );
}

Filling a container

With fill the parent decides the size. GioImage sets no positioning, so give the parent a size, for example with aspect-ratio.

app/blog/[slug]/cover.tsx
import { GioImage } from '@gio.js/react';
import styles from './cover.module.css';

export function Cover({ src, alt }: { src: string; alt: string }) {
  return (
    <div className={styles.cover}>
      <GioImage src={src} alt={alt} width={0} height={0} fill sizes="(max-width: 960px) 100vw, 960px" />
    </div>
  );
}
app/blog/[slug]/cover.module.css
.cover {
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: 12px;
}

A blurred preview

app/gallery/[id]/page.tsx
import { GioImage } from '@gio.js/react';

interface Photo {
  url: string;
  alt: string;
  width: number;
  height: number;
  preview: string; // 'data:image/webp;base64,...', made when the photo was uploaded
}

export default function PhotoPage({ photo }: { photo: Photo }) {
  return (
    <GioImage
      src={photo.url}
      alt={photo.alt}
      width={photo.width}
      height={photo.height}
      sizes="100vw"
      placeholder="blur"
      blurDataURL={photo.preview}
    />
  );
}

A remote image

Allow the host in gio.toml; anything not on the list is refused.

gio.toml
[[images.remote_patterns]]
protocol = "https"
hostname = "images.example.com"
pathname = "/uploads/*"
tsx
<GioImage src="https://images.example.com/uploads/team.jpg" alt="The team" width={800} height={533} />

An animated GIF

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

Good to know

  • The blur preview stays behind the image after it loads: transparent areas of a PNG or WebP show it. Use placeholder="blur" for opaque photos.
  • fill and placeholder="blur" render a style attribute. Under a Content Security Policy, style-src must allow 'unsafe-inline' for them.
  • The optimizer caches each width under a name that ignores the source's content: replacing public/hero.jpg keeps serving the old resized copies. Give a changed image a new file name.
  • A quality per image adds new URLs to the optimizer's cache; keep to a few values.
  • Prefer pre-sized files in a static export: every image is served as is.
  • Path traversal, redirects on remote sources and guard checks on public/ files are enforced by the optimizer and cannot be turned off; the size and time limits can (see [images]).

Version history

VersionChanges
v0.1.0-beta.8Requests only the widths in [images] allowed_widths, with [images] quality as the default. priority preloads the image. Added unoptimized; SVG, data: and blob: sources, static exports and [images] enabled = false render the plain src.
v0.1.0-beta.1Introduced.