Skip to content
Inkoxide

Search covers every page and heading of the docs. Use ↑ ↓ and ↵.

Serving templates

Edit this page

An advanced API, for a server that renders templates by id from JSON data, such as POST /pdf/invoice. It lives in its own entry, @inkoxide/render/templates, and checks the data with a zod schema before React renders anything.

import { readFileSync } from 'node:fs';

import { Document, Image, Page, RenderError, Text } from '@inkoxide/render';
import { generate, type TemplateLike } from '@inkoxide/render/templates';
import { z } from 'zod';

const schema = z.object({ number: z.string(), total: z.number() });

const invoice: TemplateLike<typeof schema> = {
  meta: { id: 'invoice' },
  schema,
  Template: ({ data }) => (
    <Document>
      <Page>
        <Image src="logo" className="h-10 w-10" />
        <Text>Invoice {data.number}: {data.total}</Text>
      </Page>
    </Document>
  ),
  images: { logo: readFileSync('logo.png') },
};

const templates = new Map<string, TemplateLike>([[invoice.meta.id, invoice]]);

app.post('/pdf/:id', async (req, res) => {
  try {
    const template = templates.get(req.params.id);
    if (!template) throw new RenderError('UNKNOWN_TEMPLATE', `No template "${req.params.id}".`);
    const { pdf } = await generate(template, req.body);
    res.type('application/pdf').send(Buffer.from(pdf));
  } catch (e) {
    if (!(e instanceof RenderError)) throw e;
    res.status(e.status).json(e);
  }
});

A template is { meta: { id }, schema, Template, images? }: the component receives the parsed data as data, and images are its static images by id (a logo). An image passed in the render options under the same id replaces the template's.

  • generate(template, data, options?) checks data, renders and resolves to { pdf, pages, passes, bytes, timings, fonts, warnings, document, images, schemaVersion, engineVersion }. timings has the milliseconds of each stage, each counted once: reactMs, treeMs (with the rebuilds for the page count), validateMs (0 when validate is off), engineMs, fetchMs and totalMs. warnings are the document's font fallbacks, hyphenate with no language, and render props that did not settle; start-up warnings are in engineStatus(). document is the tree that was rendered, and images the images it was rendered with, by id: the tree names an image given as bytes, a file, a URL or a function by an id it made up (image-0, remote-0), and images has the bytes behind those ids. The options are those of renderToBuffer.
  • parseData(template, data) only checks the data, and returns it parsed, with the schema's defaults applied.
  • renderTree(template, data, options?) builds the document tree without rendering it, for snapshots and previews. Its document is the tree, and its images the images it uses. It is synchronous, so it cannot load what a render loads first: await init() before it, and again after Font.register. Otherwise it throws an INTERNAL RenderError that says so. renderSettled(tree, images, options?) renders it, and renders again with the page count while render props change it; images are more images by id ({} when there are none), and it resolves to { pdf, pages, passes, tree, images, fetchMs, treeMs } (treeMs: the rebuilds for the page count). Pass both the same options.
  • renderDocument(document, images?, options?) renders a document tree that is already built, such as a stored result.document, and resolves to { pdf, pages, engineMs }. The document names its images by id, so pass their bytes in images or the images option (renderDocument(result.document, result.images)); the other image options do not apply.

Data that does not match the schema fails with INVALID_DATA (400) before anything renders. Its issues list every problem as { path, message, code }, with readable messages: { path: 'items[0].qty', message: 'Missing: expected a number', code: 'invalid_type' }. UNKNOWN_TEMPLATE (404) is never thrown by the package; it is there for your own lookup, as above, so that every error has the same shape.

The entry also exports the types of these: TemplateLike (a template), GenerateOptions (the options of generate), RenderResult (what generate resolves to), Timings (its timings), TreeResult (what renderTree returns) and RenderOpts (the options of renderSettled and renderDocument).

Synced from packages/render/GUIDE.md in the inkoxide repository.