@syedalihamzazaidi/renderkit
v0.2.0
Published
Render JSON design scenes (Fabric.js or Polotno-style) to PNG/JPEG/PDF buffers.
Maintainers
Readme
RenderKit
Render JSON design scenes — Fabric.js canvas JSON or Polotno-style pages — to PNG/JPEG images in Node.js, using node-canvas and fabric.js.
npm install @syedalihamzazaidi/renderkitimport { renderDesign } from '@syedalihamzazaidi/renderkit';
import { writeFile } from 'fs/promises';
const { png, jpeg } = await renderDesign({
pages: [{
width: 400,
height: 300,
background: '#ffffff',
children: [
{
type: 'text',
x: 20, y: 20,
width: 360, height: 60,
text: 'Hello RenderKit',
fontFamily: 'Roboto',
fontSize: 32,
fontWeight: '700',
fill: '#111111',
align: 'center',
},
{
type: 'figure',
subType: 'rect',
x: 50, y: 100,
width: 300, height: 150,
fill: '#4f46e5',
cornerRadius: 12,
},
],
}],
});
await writeFile('output.png', png);png and jpeg are Buffers — do whatever you want with them: save to disk, upload to storage, stream in an HTTP response, etc.
Also included in this repo: a ready-to-run NestJS HTTP server that wraps the same rendering engine, for teams who'd rather call a REST endpoint than embed the library. See Run as a server below.
Requirements
- Node.js >= 18
canvas's native build dependencies for your OS (Cairo, Pango, etc.) — see the node-canvas installation guide- Outbound internet access at render time if you use Google Fonts (see Fonts)
API
renderDesign(design, options?)
function renderDesign(design: unknown, options?: RenderOptions): Promise<RenderResult>;
interface RenderOptions {
/** Also write output.png / output.jpg to this directory. */
outDir?: string;
/** JPEG quality, 0-1. Defaults to 0.9. */
jpegQuality?: number;
}
interface RenderResult {
png: Buffer;
jpeg: Buffer;
/** Present only when `outDir` was provided. */
files?: { png: string; jpeg: string };
}Throws RenderError (also exported) when the design JSON is invalid or rendering fails.
import { renderDesign, RenderError } from '@syedalihamzazaidi/renderkit';
try {
const result = await renderDesign(design, { outDir: './output' });
console.log(result.files); // { png: './output/output.png', jpeg: './output/output.jpg' }
} catch (err) {
if (err instanceof RenderError) {
console.error('Invalid design:', err.message);
}
}Design JSON schema
A design is either a full document ({ "pages": [...] }, first page used) or a single page object directly. Each page needs:
width,height— required numbersbackground— optional CSS color string- One of:
json— a raw Fabric.js canvas JSON object (passed straight tocanvas.loadFromJSON), orchildren— an array of Polotno-style elements (converted to Fabric internally)
Polotno children element types
Common fields on every element: type, x, y, rotation, opacity, visible (elements with visible: false are skipped).
type: "text"
| Field | Notes |
|---|---|
| text | string content |
| width, height | box size |
| fontSize | number |
| fontFamily | any Google Fonts family name (fetched live, see below) |
| fontWeight | number or string, default "400" |
| fontStyle | "italic" or other |
| align | text alignment |
| fill | text color |
| textDecoration | "underline" or "line-through" |
| lineHeight, letterSpacing | numbers |
type: "image" / "svg"
| Field | Notes |
|---|---|
| src | required — URL, data URI, or resolvable path |
| width, height | used to compute scaleX/scaleY against natural image size |
| flipX, flipY | booleans |
type: "figure"
| Field | Notes |
|---|---|
| subType | "circle" or "rect" (default "rect") |
| width, height | shape size |
| fill | fill color |
| stroke, strokeWidth | stroke (ignored if stroke is "transparent") |
| cornerRadius | rect only |
Any other type is silently dropped.
Fonts
Text elements load fonts on demand from Google Fonts (https://fonts.googleapis.com/css2?family=...), download the .ttf, register it with node-canvas, and cache it on disk under .font-cache/ in the current working directory. This means:
- Your process needs outbound network access at render time for any font not already cached.
- Only fonts published on Google Fonts are supported; unsupported families/weights/styles fail silently and fall back to the canvas default — no error is raised.
- Fabric-JSON-only payloads (
page.json) never trigger font loading.
Current limitations
- Only PNG and JPEG output are implemented — no PDF or SVG export yet.
- Font loading and
outDirwrites touch the filesystem/network — for a pure in-memory environment, stick topage.json(Fabric JSON) input and ignorefiles/outDir.
Run as a server
This repo also ships the NestJS app that exposes renderDesign over HTTP, if you'd rather run a service than embed the library.
git clone https://github.com/codewithalihamza/renderkit.git
cd renderkit
npm install
npm run start:devEnvironment variables
| Variable | Default | Description |
|------------|---------------|-------------------------------|
| PORT | 5000 | HTTP port the server binds to |
| NODE_ENV | development | Environment name |
Endpoints
GET /health
curl http://localhost:5000/healthPOST /render — body is a design JSON object (see schema above).
curl -X POST http://localhost:5000/render \
-H "Content-Type: application/json" \
-d '{"pages":[{"width":400,"height":300,"background":"#ffffff","children":[{"type":"figure","subType":"rect","x":50,"y":100,"width":300,"height":150,"fill":"#4f46e5","cornerRadius":12}]}]}'{
"success": true,
"data": { "success": true, "png": "output/output.png", "jpeg": "output/output.jpg" },
"timestamp": "..."
}The png/jpeg fields are paths relative to the server's working directory — the endpoint does not stream image bytes back. Files are written to <project root>/output/output.png and output.jpg; without a shared volume, they won't be reachable across multiple server instances.
GET /render renders a fixed design file at <project root>/raffle-ticket-design.json, which is not included in this repo — add your own JSON at that path first, or this route returns 404.
Build scripts
npm run build # nest build (also compiles the library entry point)
npm run start:prod # node dist/mainAuthor
Ali Hamza — LinkedIn
License
MIT
