@veztraa/report-renderer
v0.3.4
Published
Render JSON report templates to PDF in Node.js or the browser — zero Chromium, zero Python. Supports 12 element types including signature blocks, repeating container bands, and multilingual text (Chinese, Japanese, Korean, Arabic, Hebrew, Hindi, Thai) via
Maintainers
Readme
@veztraa/report-renderer
PDF rendering engine for ReportForge. Converts a template JSON + data object into a PDF buffer — pure Node.js, zero Chromium, zero Python.
Works in Node.js and the browser from a single install. Supports 12 element types including signature blocks and repeating container bands. Fonts (12 Latin families, 4 handwriting fonts, 8 multilingual Noto fonts) are registered automatically — no manual setup needed.
Installation
npm install @veztraa/report-rendererUsage
Node.js (Express, Next.js API route, CLI, etc.)
import { render } from "@veztraa/report-renderer";
const pdf = await render(template, data);
// Save to file
import fs from "fs";
fs.writeFileSync("report.pdf", pdf);Next.js API route
import { NextRequest, NextResponse } from "next/server";
import { render } from "@veztraa/report-renderer";
export async function POST(req: NextRequest) {
const { template, data } = await req.json();
if (!template || !data) {
return NextResponse.json(
{ success: false, message: "Both 'template' and 'data' are required." },
{ status: 400 }
);
}
const pdfBuffer = await render(template, data);
return new NextResponse(pdfBuffer as any, {
status: 200,
headers: {
"Content-Type": "application/pdf",
"Content-Disposition": 'attachment; filename="report.pdf"',
},
});
}Express
import express from "express";
import { render } from "@veztraa/report-renderer";
const app = express();
app.use(express.json());
app.post("/api/pdf", async (req, res) => {
const { template, data } = req.body;
const pdf = await render(template, data);
res.setHeader("Content-Type", "application/pdf");
res.setHeader("Content-Disposition", "attachment; filename=report.pdf");
res.send(pdf);
});Browser (React + Vite / Next.js client)
import { ReportDocument, usePDF } from "@veztraa/report-renderer";
function DownloadButton({ template, data }) {
const doc = <ReportDocument template={template} data={data} />;
const [{ url, loading }] = usePDF({ document: doc });
if (loading) return <span>Rendering…</span>;
return <a href={url} download="report.pdf">Download PDF</a>;
}Bundlers (Vite, webpack) automatically pick the browser build via the
browserexport condition. Latin and handwriting fonts are embedded as base64 data URIs — no network requests, works offline. Noto multilingual fonts load from CDN on first use.
Pagination
The engine flows content-driven elements across pages automatically — no manual page math:
- Tables break between rows and repeat the header on each continuation page.
- Rich text breaks between lines; headings, images, and embedded tables are never split mid-element.
- Repeating containers (
type: "container"with adataSource) flow item by item: an item that fits on a page is kept together, while an item taller than a page flows across pages.
Large reports & the browser main thread
render() / usePDF() lay the document out synchronously, so a very large report (hundreds of pages) will block the thread it runs on. In Node/CLI that's fine. In the browser, render inside a Web Worker to keep the UI responsive:
// worker-globals.ts — imported first so it runs BEFORE react-pdf loads.
const g = globalThis as any;
if (typeof g.window === "undefined") g.window = g;
export {};// pdf.worker.ts
import "./worker-globals"; // MUST be the first import
import React from "react";
import { pdf, ReportDocument } from "@veztraa/report-renderer";
self.onmessage = async (e) => {
const { template, data } = e.data;
const blob = await pdf(React.createElement(ReportDocument, { template, data })).toBlob();
self.postMessage({ blob });
};
@veztraa/report-designeralready renders its live preview in a worker — you only need this if you call the renderer directly in the browser.
API
// Render to Buffer (Node.js) or Uint8Array (browser)
render(template: Template, data?: Record<string, unknown>): Promise<Buffer>
// Render to readable stream (Node.js only)
renderStream(template: Template, data?: Record<string, unknown>): Promise<NodeJS.ReadableStream>
// React component — use with usePDF in the browser
ReportDocument: React.FC<{ template: Template; data: Record<string, unknown> }>
// Re-exported from the underlying PDF engine for browser usage
usePDF, pdf, BlobProviderBundled Fonts
Latin & built-in (bundled — no network request)
| Family | Weights | |---|---| | Helvetica, Times Roman, Courier | Built-in (no download) | | Roboto | 400, 700, italic | | Open Sans | 400, 700, italic | | Lato | 400, 700, italic | | Montserrat | 400, 700, italic | | Inter | 400, 700 | | Poppins | 400, 700, italic | | Nunito | 400, 700, italic | | Raleway | 400, 700, italic | | Ubuntu | 400, 700, italic | | Playfair Display | 400, 700, italic | | Merriweather | 400, 700, italic | | Source Code Pro | 400, 700 |
Handwriting (bundled — for signature elements)
| Family | Use case | |---|---| | Great Vibes | Elegant cursive — formal signatures | | Dancing Script | Casual handwriting | | Pacifico | Rounded, friendly script | | Sacramento | Thin calligraphic script |
Use any handwriting family as style.fontFamily in a text element, or as scriptFont in a signature element.
Multilingual Noto fonts (CDN-loaded on first use)
@react-pdf has no automatic glyph fallback — you must pick a font family whose file contains your content's script glyphs. The Noto families below each cover one script:
| Family | Script |
|---|---|
| Noto Sans | Latin, Cyrillic, Greek |
| Noto Sans SC | Chinese Simplified (also covers Latin) |
| Noto Sans JP | Japanese (also covers Latin) |
| Noto Sans KR | Korean (also covers Latin) |
| Noto Sans Arabic | Arabic |
| Noto Sans Hebrew | Hebrew |
| Noto Sans Devanagari | Hindi / Devanagari |
| Noto Sans Thai | Thai |
CJK fonts (SC, JP, KR) also include automatic per-character line-breaking so text wraps correctly even without spaces.
Template Format
See @veztraa/report-core for the full template schema and expression syntax.
Keywords
pdf pdf-generator pdf-renderer report nodejs-pdf react-pdf pdf-export no-chromium no-puppeteer server-side-pdf nextjs-pdf express-pdf signature multilingual reportforge
License
MIT © Veztraa Solutions
