---
title: "@commentware-private/documents"
description: "The functions the template library exports, for writing a template and for rendering one to a file. Experimental."
---

> Documentation Index
> Fetch the complete documentation index at: https://commentware.com/llms.txt
> Use this file to discover all available pages before exploring further.

# @commentware-private/documents

`@commentware-private/documents` exports the functions that define a template
and render it to a PDF or an XLSX file.

The library is experimental, so a function may change or be removed in any release, with no notice first. A project imports each by that name. `react`, `react-dom`, `zod` and `exceljs`
are peers: a template builds its elements, schema and workbook with the
project's own copies, so the project installs them beside the library.

## `defineTemplate`

**Usage:** `defineTemplate({ schema, supportedOutputs, mockData, config })`

Defines a template. A template file's default export is its result.

- `schema` is a zod schema for the params. Every render checks the params
  against it.
- `supportedOutputs` lists `"pdf"`, `"xlsx"` or both. A template that lists
  `"xlsx"` also renders to `"pdf"`, printed from its workbook.
- `mockData` is params the schema accepts, which the preview starts from.
- `config({ params })` answers the template's `metadata` and `renderers` for
  those params:
  - `metadata.fileName` is the file's name without its extension.
  - `metadata.pdf` is `{ name, width, height, margin? }`, the page as CSS
    lengths in `mm`, `cm`, `in`, `pt` or `px`, and `margin` is
    `{ top?, right?, bottom?, left? }` in the same lengths, each `0` by
    default. A template with a component needs `metadata.pdf`. Every page of
    the PDF is exactly this size.
  - `metadata.xlsx` is `{ sheetName?, pdf? }`, where `pdf` lays the workbook
    out when it is printed: `fit` is `"scale-to-fit"`, the default, or
    `"auto"`, and `align` is `"left"`, the default, `"center"` or `"right"`.
    A printed workbook is on an A4 page with `10mm` margins unless
    `metadata.pdf` names another page.
  - `renderers.pdf` is `{ component }`, where `component` is an async function
    returning the React element drawn on the page, or the result of
    [`renderXlsxToPdf`](#renderxlsxtopdf).
  - `renderers.xlsx` is `{ workbook }`, where `workbook` is an async function
    returning an `exceljs` workbook.

This template builds a spreadsheet of SKUs and prices:

```tsx title="src/templates/price-list.tsx"
import ExcelJS from "exceljs";
import { z } from "zod";
import { defineTemplate, xlsxStyles } from "@commentware-private/documents";

export default defineTemplate({
  schema: z.object({
items: z.array(z.object({ sku: z.string(), price: z.number() })),
  }),
  supportedOutputs: ["xlsx"],
  mockData: { items: [{ sku: "WA-001", price: 29.99 }] },
  config: async ({ params }) => ({
metadata: { fileName: "price-list", xlsx: { sheetName: "Prices" } },
renderers: {
  xlsx: {
    workbook: async () => {
      const workbook = new ExcelJS.Workbook();
      const sheet = workbook.addWorksheet("Prices");
      sheet.addRow(["SKU", "Price"]).font = xlsxStyles.headerRow.font;
      for (const item of params.items) {
        sheet.addRow([item.sku, item.price]).getCell(2).numFmt =
          xlsxStyles.currency;
      }
      return workbook;
    },
  },
},
  }),
});
```

## `defineConfig`

**Usage:** `defineConfig({ templates, ... })`

Types the default export of `documents.config.ts`. Every option is on
[documents.config.ts](/docs/documents/config/).

## `createRenderer`

**Usage:** `createRenderer({ templates, stylesheet?, browser? })`

Returns a renderer for a project's templates. `templates` and `stylesheet` are
the two exports of the `templates.ts` that `commentware-documents build`
writes; `browser` is `{ executablePath? }`, as in the config. The renderer's
types know every template's id, its params and its outputs, so a wrong one does
not compile.

## `render`

**Usage:** `renderer.render(id, params, output)`

Renders one template to `"pdf"` or `"xlsx"` and answers `{ bytes, fileName,
mimeType }`, where `fileName` carries the extension. The first PDF starts
Chrome, which stays up until [`close`](#close).

Params the template's schema does not accept are refused, with each problem at
its path:

```text
The params for "thank-you" do not match its schema:
✖ Invalid input: expected string, received number
  → at name
```

An id no template has is refused with the ids there are, as in
`No template has the id "thank-yuo". The templates are: price-list, thank-you.`,
and an output the template does not render to with the ones it does, as in
`"thank-you" does not render to xlsx. It renders to pdf.`

## `metadata`

**Usage:** `renderer.metadata(id, params)`

Answers the template's `metadata` for those params, and every output it
renders to, without rendering it. For the price list above:

```text
{
  fileName: 'price-list',
  xlsx: { sheetName: 'Prices' },
  supportedOutputs: [ 'xlsx', 'pdf' ]
}
```

## `close`

**Usage:** `renderer.close()`

Stops the Chrome that PDFs are printed with. A later PDF starts it again.

## `renderXlsxToPdf`

**Usage:** `renderXlsxToPdf(workbook, options?)`

Makes a template's PDF a print of a workbook. `workbook` is an async function
returning an `exceljs` workbook, as `renderers.xlsx.workbook` is, and `options`
takes the same `fit` and `align` as `metadata.xlsx.pdf`. Use it as
`renderers.pdf` when the PDF prints a different workbook from the XLSX file:
`pdf: renderXlsxToPdf(summaryWorkbook, { fit: "auto" })`.

## `fetchAndAddImage`

**Usage:** `fetchAndAddImage(workbook, sheet, url, range, options?)`

Fetches the image at `url`, converts an SVG to PNG because Excel draws no SVG,
fits it to the cell `range`, such as `"A1:B3"`, and places it. `options.fit` is
`"fill"`, the default, `"contain"` or `"cover"`; `options.position` places a
contained or covered image, `"center"` by default; `options.editAs` is
`"twoCell"`, the default, `"oneCell"` or `"absolute"`, as `exceljs` takes it. A
range that is not two cell addresses is refused, naming it.

## `addImageToWorksheet`

**Usage:** `addImageToWorksheet(workbook, sheet, { buffer, extension, range, editAs?, imageWidth?, imageHeight? })`

Places image bytes the template already has, as a `"png"`, `"jpeg"` or `"gif"`,
on the cell `range`, unchanged. With `editAs` of `"oneCell"` or `"absolute"`,
`imageWidth` and `imageHeight` in pixels keep Excel from stretching it.

## `xlsxStyles`

**Type:** an object

Styles a workbook template reuses: `headerRow`, a bold `font` and a centered
`alignment`, and the number formats `currency`, `percentage`, `date` and
`dateTime`.