Images
import { readFileSync } from 'node:fs';
const logo = readFileSync('logo.png');
<Image src={logo} className="h-10 w-10" fit="contain" /> // bytes: Uint8Array, Buffer, ArrayBuffer
<Image src="data:image/png;base64,iVBORw0…" /> // a base64 data: URL
<Image src="logo" /> // an id from options.images
<Image src="./images/logo.png" /> // a file, when allowed
<Image src="https://cdn.example.com/photo.jpg" /> // remote, when allowed
<Image src={{ uri: 'https://cdn.example.com/chart', method: 'POST', headers, body }} />
<Image src={() => loadLogo()} /> // a function or a promise
-
Fit and position.
fit(orobjectFit, orobject-contain,object-cover,object-fill,object-none) sizes the image in its box:fill(the default) stretches it (an SVG with aviewBoxis not stretched: as in CSS, the box becomes its viewport, and itspreserveAspectRatio, by default centred and whole, fits the drawing in),containshows all of it,coverfills the box,nonekeeps its own size (96 dpi). Withcontain,coverandnone, the image is centred unlessobjectPositionplaces it, as CSSobject-positiondoes: one or two values, each a keyword (left,center,right,top,bottom, in either order when both are keywords), a length or a percentage ('25% 75%'lines up that point of the image with that point of the box). A single value leaves the other axis centred. The Tailwind classesobject-left-top,object-right,object-bottomand so on, andobject-[25%_75%], do the same. What falls outside the box is clipped. Edge offsets ('right 10pt bottom 5pt') are not supported.<Image src={photo} className="h-40 w-full" fit="cover" style={{ objectPosition: 'center top' }} /> <Image src={logo} className="h-10 w-32 object-contain object-left" /> -
Size. An
Imagewith nowidthorheightis as big as the image is (96 dpi), but no wider than the room it has: when the image is wider, it is scaled down to fit, keeping its aspect ratio. CSS would let it overflow. With one of them, the other follows the ratio. -
Images in text. An
Imageinside aTextsits in the line, like a character: lines break before and after it. With neitherwidthnorheightit is 1em high (the text's size) and as wide as its aspect ratio gives; with one of them (in points or em), the other follows the ratio. Percentages and box styles (margins, padding, borders) are errors there.fit,objectFitandobjectPositionwork as on a block image,altis the text it copies and searches as, and aLinkaround it makes it a link.verticalAlignplaces it:baseline(the default, its bottom on the baseline),middle(its middle half an x-height above the baseline),text-topandtext-bottom(its top at the font's ascender, its bottom at its descender),topandbottom(at the line's top or bottom),superandsub, or a number of points its bottom sits above the baseline. The line grows to fit it. SVG images work too.<Text> Rated <Image src={star} style={{ height: '0.9em', verticalAlign: 'middle' }} alt="4 stars" /> by <Link href="https://example.com"><Image src={logo} className="align-text-bottom" /></Link> </Text> -
Background images.
ImageBackgroundis aViewwith an image behind its children. It takes the props of aViewandsrcin any formImagetakes;fit,objectFitandobjectPosition(and theobject-*classes) place the image as on anImage,fillby default. The image fills the padding box, inside the borders, as a CSSbackground-imagedoes: it is drawn over the background colour and under the borders and the children, and clipped to the box, rounded byborderRadius. A box split across pages is sliced with its image, like its background colour.source,imageStyleandsrcSetthrow: pass the image assrc, and place it withfitandobjectPosition.<ImageBackground src={photo} fit="cover" className="h-48 rounded-xl p-6 object-top"> <Text className="text-2xl font-bold text-white">Annual report</Text> </ImageBackground> -
Files. Image files are off by default. Allow the folders they come from with the
localImagesoption, or theINKOXIDE_LOCAL_IMAGESenvironment variable (folders separated by:, or;on Windows):await renderToBuffer(<Doc />, { localImages: { root: '/srv/app/images' } });A string is then a file path when it is written as one: relative with
./or../, absolute (/srv/app/images/logo.png,C:\images\logo.png), or afile:URL. Any other string (logo,logo.png,img/logo.png) is an image id, so write./logo.pngfor a file. WithoutlocalImages, a path is an image id too, and no file is read. -
Folders. A relative path is resolved against the first
root(an array allows several), not the working directory. Any path must lead, symbolic links followed, to a regular file inside one of them. Network paths (\\host\share,//host/share,file://host/…) are refused, and so is a file overmaxBytes(default 20 MiB). The error names the path as the template wrote it, and is the same for a missing file and a file outside the folders:Cannot read the image ../secret.png: no such file in the localImages folders.(IMAGE_READ_FAILED). -
Ids first. A string that names an image in the
imagesoption (or a template'simages) is that image, even when it looks like a path or a URL. No file is read for it. -
Requests.
{ uri, method, headers, body }fetchesuriwith that method, headers and body. It follows the same rules as a URL string: the allowlist, the timeout, the size limit and no redirects. -
Redirects. A remote image is not followed to another URL, since that URL could be outside the allowlist: a redirect fails the render with
IMAGE_FETCH_FAILED(HTTP 302, and redirects are not followed). Use the URL the image ends up at. -
Remote limits.
remoteImages.timeoutMs(default 10 000 ms;Infinitywaits without a limit) covers the whole download, body included, andmaxBytes(default 20 MiB) its size. A download that times out, breaks off or grows past the limit fails withIMAGE_FETCH_FAILED. -
Functions and promises. A function is called once per render, after the tree is built and before the engine runs, with the other images loading in parallel; a promise is awaited then. Either resolves to bytes, a string or a request, read as if written in
src. If it throws, rejects or does not settle withinimageTimeoutMs(default 10 000 ms), the render fails withTEMPLATE_ERROR. Arenderprop that creates a new function or promise each time has it loaded again when it renders again for the page count. Prefer a function to a promise made before the render starts: a promise that rejects while nothing awaits it yet is an unhandled rejection.
Image files and remote images are off by default, so a template cannot make your server read
its files or fetch arbitrary URLs. Allow only the folders and hosts your images are in. An entry
allows only the default port of its scheme unless it names one (cdn.example.com:8443). Remote
images are allowed per render, or with the INKOXIDE_REMOTE_IMAGES environment variable (a
comma-separated list):
await renderToBuffer(<Doc />, { remoteImages: { allow: ['https://cdn.example.com/', '*.example.org'] } });Synced from packages/render/GUIDE.md in the inkoxide repository.