Define each PDF or XLSX file your Node project produces in a file of its own, see it while you edit it, and render it from your own server. Do this when your project sends quotes, letters or price lists.
A file defined in code this way — the params it takes, sample params, and how it draws itself — is called a template. The library works on its own: it needs no Commentware server and no extension. It is experimental: its functions, config options and commands may change or be removed in any release, with no notice first, and its preview’s page carries an Experimental label.
Prerequisites
- Node 24 or later, and a project with a
package.jsonwhose code is TypeScript or JavaScript with React’s JSX. A TypeScript project sets"jsx": "react-jsx"in itstsconfig.json. - A package manager. npm comes with Node.
Install the library
-
Point the
@commentware-privatescope at the package registry,https://registry.commentware.com, so that your package manager fetches the library from it rather than from npm. Run the command for your package manager, or for yarn and bun write the file it shows:pnpm config set @commentware-private:registry=https://registry.commentware.com/It writes ~/.npmrc, the file the install below reads. No flag: that is where this command goes by default.
npm config set @commentware-private:registry=https://registry.commentware.com/It writes ~/.npmrc, the file the install below reads. No flag: that is where this command goes by default.
In
~/.yarnrc.ymlnpmScopes: commentware-private: npmRegistryServer: "https://registry.commentware.com/"Yarn 2 and later ignores .npmrc. `yarn config set … --home` writes this file, but only from yarn 2 and later: yarn 1 takes the same command, says it worked, and writes somewhere yarn 2 never reads.
In
~/.npmrc@commentware-private:registry=https://registry.commentware.com/bun reads ~/.npmrc and has no command that writes it. npm's command above produces exactly this line.
-
In your project, add the library and the four packages your templates are built with:
pnpm add @commentware-private/documents react react-dom zod exceljsnpm install @commentware-private/documents react react-dom zod exceljsyarn add @commentware-private/documents react react-dom zod exceljsbun add @commentware-private/documents react react-dom zod exceljs -
A PDF is printed by Chrome. If you want the library to use a Chrome or Chromium you already have, name it in
browser.executablePathand skip this step. Otherwise, download the Chrome for Testing build it uses:npx commentware-documents install-browserIt prints each download, about 650 MB in all, and ends with
Chrome for Testing is installed.On a machine that already has the build, it prints that line alone.
Write a template
-
At the root of your project, create the config file that names the directory your templates are in:
documents.config.tsts import { defineConfig } from "@commentware-private/documents"; export default defineConfig({ templates: ["src/templates"], }); -
Create a template in that directory. Its id is its path inside the directory without the extension, here
thank-you:src/templates/thank-you.tsxtsx import { z } from "zod"; import { defineTemplate } from "@commentware-private/documents"; export default defineTemplate({ schema: z.object({ name: z.string() }), supportedOutputs: ["pdf"], mockData: { name: "Mira" }, config: async ({ params }) => ({ metadata: { fileName: `thank-you-${params.name.toLowerCase()}`, pdf: { name: "Thank you", width: "148mm", height: "210mm" }, }, renderers: { pdf: { component: async () => ( <div className="p-[15mm] text-2xl">Thank you, {params.name}.</div> ), }, }, }), });Tailwind classes work in a template. Every field is in
defineTemplate.
Preview the template
-
From the root of your project, start the preview:
npx commentware-documents previewIt prints where it serves and keeps running until you press Ctrl-C:
Previewing 1 template at http://127.0.0.1:7043/ Experimental: the template library and these commands may change or go in any release, with no notice first.If something else holds
7043, start it with--portand a free port. -
Open
http://127.0.0.1:7043/and selectthank-you. Edit the params on the left, or the template’s file, and the page redraws. Download PDF gives you the file.Optional: to see the page as printed pages, select Pages in the toolbar. Each printed page is the size the template’s
pdfnames, with its margins, and the text stays text, so a Commentware comment pins to it. The preview remembers the choice in this browser.
Render it from your code
-
Write the index your code renders through. Run it again whenever you add a template or a Tailwind class:
npx commentware-documents buildWrote .documents/templates.ts: 1 template, 4 KB of CSS..documents/is generated. Add it to your.gitignore. -
Render the template where your code needs the file. The params are checked against the template’s schema, and a mismatch is refused with each problem named:
render.tsts import { writeFile } from "node:fs/promises"; import { createRenderer } from "@commentware-private/documents"; import { stylesheet, templates } from "./.documents/templates"; const renderer = createRenderer({ templates, stylesheet }); const file = await renderer.render("thank-you", { name: "Ada" }, "pdf"); await writeFile(file.fileName, file.bytes); console.log(`Wrote ${file.fileName}`); await renderer.close();The renderer starts Chrome on the first PDF and keeps it until
close(), so a server creates one renderer and renders through it.
Check that it worked
Run render.ts with the TypeScript runner your project uses, tsx in this
example:
npx tsx render.tsWrote thank-you-ada.pdfOpen thank-you-ada.pdf. It is one A5 page reading “Thank you, Ada.”
Next steps
- documents.config.ts: add a stylesheet, more template directories, or the Vite plugins your templates import with.
- @commentware-private/documents: write a template that builds a spreadsheet, and render it to XLSX.
- commentware-documents: every option of
previewandbuild.