[]previous and next page

Icons and social images

A favicon or an apple touch icon is a file in public/ and an icons entry in the layout's metadata.

For agentsUpdated

A favicon or an apple touch icon is a file in public/ and an icons entry in the layout's metadata. A generated image is an API route that returns an ImageResponse. It draws an element tree with satori (layout) and resvg (rasterization). Both are optional peer dependencies of @shadow-js/dusk: install satori and @resvg/resvg-wasm where the image is drawn.

src/routes/og.png.tsts
import { h, ImageResponse } from "@shadow-js/dusk/og";

const sans = new Uint8Array(0);
const wasm = new Uint8Array(0);

export const GET = () =>
  new ImageResponse(
    h(
      "div",
      { style: { display: "flex", width: "100%", height: "100%", background: "#0b0b0a", color: "#f1f0ec", padding: 72 } },
      h("div", { style: { fontSize: 72 } }, "Example"),
    ),
    { fonts: [{ name: "Sans", data: sans, weight: 400 }], resvgWasm: wasm },
  );

ImageResponse answers image/png at 1200 by 630 by default (width and height change it; each side is a whole number up to 4096). It uses cache-control: public, max-age=3600, stale-while-revalidate=86400 unless a header is set. The tree is checked when the response is built: a tag name satori cannot draw, a tree deeper than 200 levels or larger than 5000 nodes is refused before any drawing. Text needs at least one font (TrueType, OpenType or WOFF; satori does not read WOFF2). The resvg wasm is the index_bg.wasm of @resvg/resvg-wasm, passed as bytes or a module.

What a tree may hold. Satori fetches every image a tree names while it draws, so a URL that a request can influence would be a request from your server. The tree is therefore checked when the response is built:

  • An img src, an href or xlink:href, a srcSet, and any CSS url() (in a style value or a tw class) must be a data:image/... URI (or a #fragment inside an svg). Anything else is a TypeError. Fetch the image yourself, check it, and pass its bytes as a data: URI. allowRemoteImages: true lets http(s) URLs through for addresses written in code; never use it with a URL built from a path, a query, a header or a database row. file:, javascript: and protocol-relative values stay refused. A CSS escape (\75rl() and image-set() are refused too.
  • Text is bounded: 100 000 characters in all, 10 000 in one style or attribute value, and data: images of 4 MiB each and 8 MiB in all. The tree has at most 5000 nodes and 200 levels. A number must be finite, and a function, symbol or bigint in a prop is refused.
  • resvgWasm is the bytes or the compiled module, never a URL (initWasm would fetch a string, a URL or a Response), and the wasm is the one you pass, not one named by the request. Before resvg reads the SVG satori wrote, every href and url() in it must be a data: image or a #fragment; the check guards a satori version or a custom renderer other than the one tested here.
  • The text a request controls (a title from ?title=) is safe in the tree: satori turns text into outlines, and a quote or a tag in a style value is escaped in the attribute it ends up in. The answer is always a nosniff PNG, and a font's data must be bytes.

The tree is built with h, or with jsx and jsxs from @shadow-js/dusk/og. ShadowJS JSX compiles to DOM templates, so a .tsx file in a ShadowJS project cannot return an image tree; keep the image in a .ts file.

Non-Latin text: a font stack#

fonts takes several faces, and each glyph is drawn with the first face in the list that has it. A face named in an element's fontFamily is the first choice for that element, and the rest of the list is the fallback, glyph by glyph. A text with no fontFamily uses the faces in order. So a Latin face and an Arabic face together draw a title that mixes the two, and without the Arabic face the Arabic letters are not drawn. The Arabic face must be a static TrueType or OpenType file: satori reads a font's default instance, and a variable font whose fvar table does not parse is refused by the font reader. Noto Sans Arabic or Noto Naskh Arabic, Regular weight, are suitable (SIL Open Font License).

A render error after the response starts ends the body with that error. The HTTP status has already been sent and cannot be changed.