
Generate PPTX from JSON in 5 minutes
Convert a JSON object to a PowerPoint file in 5 minutes with PaperJSX. Install, define slides as JSON, call generate(), and write the buffer.
PaperJSX converts a JSON object to a valid PowerPoint file with one function call. Install @paperjsx/json-to-pptx, define your slides as a JSON schema with text, charts, images, and tables, call generate(), and write the resulting buffer to disk or return it from an API endpoint. The entire flow takes under 5 minutes.
Prerequisites
- Node.js 18 or later (LTS recommended)
- npm, yarn, or pnpm
- A text editor
No native dependencies, no binary installs, no LibreOffice subprocess. PaperJSX is pure JavaScript — it runs anywhere Node.js runs, including Vercel Functions, AWS Lambda, Cloudflare Workers, Deno, and Bun.
1. Install
npm install @paperjsx/json-to-pptx
The package is <5 MB
with zero native dependencies. Compare this to Aspose's per-format SDKs at 50–200 MB each, or wkhtmltopdf-based solutions that require headless browser binaries. According to a 2025 survey by Vercel, cold start time in serverless functions correlates directly with bundle size — lighter packages mean faster first-request latency.
2. Define slides as JSON
A PaperJSX document is a plain JavaScript object. The root has a slides array. Each slide has an elements array. Each element has a type and type-specific properties.
const doc = {
slides: [
{
elements: [
{
type: "text",
value: "Hello from PaperJSX",
style: {
fontSize: 36,
bold: true,
color: "#1A1A18"
}
},
{
type: "text",
value: "Generated from a JSON schema in Node.js",
style: {
fontSize: 18,
color: "#6B6960"
}
}
]
}
]
};
This is the minimum viable document — one slide, two text elements. The JSON is portable: you can store it in a database, generate it from a template, return it from an API, or have an AI agent produce it.
3. Generate the PPTX
import { generate } from "@paperjsx/json-to-pptx";
import { writeFileSync } from "node:fs";
const doc = {
slides: [
{
elements: [
{
type: "text",
value: "Hello from PaperJSX",
style: { fontSize: 36, bold: true }
}
]
}
]
};
const buffer = await generate(doc);
writeFileSync("output.pptx", buffer);
console.log("Done — output.pptx written");
node generate.mjs
That's it. Open output.pptx in PowerPoint, Keynote, Google Slides, or LibreOffice Impress. The file is valid OOXML — no repair dialog, no corruption.
The generate() function returns a Buffer in Node.js. It does not write to disk automatically — you control where the output goes. This makes it trivial to return the PPTX from an HTTP endpoint, upload it to S3, or pipe it to a stream.
4. Serve from an API route
In production, PPTX generation typically runs server-side behind an API endpoint. Here is a minimal Express route and a Next.js API route that both generate and return a PPTX file.
import express from "express";
import { generate } from "@paperjsx/json-to-pptx";
const app = express();
app.use(express.json());
app.post("/api/generate-pptx", async (req, res) => {
const buffer = await generate(req.body);
res.set({
"Content-Type": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
"Content-Disposition": 'attachment; filename="report.pptx"',
});
res.send(buffer);
});
app.listen(3000);
import { generate } from "@paperjsx/json-to-pptx";
import { NextResponse } from "next/server";
export async function POST(req: Request) {
const doc = await req.json();
const buffer = await generate(doc);
return new NextResponse(buffer, {
headers: {
"Content-Type": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
"Content-Disposition": 'attachment; filename="report.pptx"',
},
});
}
The MIME type for PPTX files is application/vnd.openxmlformats-officedocument.presentationml.presentation. Setting Content-Disposition to attachment triggers a download in the browser rather than trying to render the binary content inline.
5. Add charts, images, and tables
Text-only slides are a starting point. Production presentations need charts, images, and tables. Each is an element type in the JSON schema.
Charts
PaperJSX generates native, editable OOXML charts — not images. According to internal benchmarks, a 10-slide deck with 5 charts generates in under 200ms
on a 2-core serverless function. Recipients can click the chart in PowerPoint and modify the data directly.
{
type: "chart",
chartType: "bar",
data: {
categories: ["Q1", "Q2", "Q3", "Q4"],
series: [
{ name: "Revenue", values: [1200, 1800, 2400, 3100] },
{ name: "Expenses", values: [800, 1100, 1500, 1900] }
]
},
style: {
width: 8,
height: 4.5,
showLegend: true
}
}
Supported chart types on the free tier include bar, line, pie, scatter, and area. Pro - Single ($79/mo) unlocks extended chart and document features for PPTX — capabilities that python-pptx has been unable to deliver since 2017.
Images
Images can be passed as file paths, URLs, or base64-encoded strings.
{
type: "image",
src: "./logo.png",
style: {
width: 2,
height: 1,
x: 0.5,
y: 0.5
}
}
Supported formats: PNG, JPEG, SVG, GIF, and WebP. SVG images are embedded as vector graphics and remain crisp at any zoom level. PaperJSX does not require images to be base64-encoded (unlike pdfmake, which requires base64 for all images).
Tables
{
type: "table",
headers: ["Region", "Q3 Revenue", "Q3 Growth"],
rows: [
["North America", "$4.2M", "+12%"],
["EMEA", "$3.1M", "+8%"],
["APAC", "$2.8M", "+22%"]
],
style: {
headerBackground: "#4580B0",
headerColor: "#FFFFFF",
alternateRowFill: "#F5F4F0"
}
}
Tables auto-size column widths based on content length. Unlike pdfmake, which has had an open issue for tables wider than the page width since 2015, PaperJSX handles overflow automatically by adjusting column proportions.
Complete JSON schema reference
The full PPTX schema is documented in the PPTX package guide. Here is a summary of all element types.
| Element type | Key properties | Tier |
|---|---|---|
text | value, style (fontSize, bold, italic, color, align) | Free |
image | src (path, URL, or base64), style (width, height, x, y) | Free |
shape | shapeType (rect, ellipse, arrow), style (fill, stroke) | Free |
table | headers, rows, style (headerBackground, alternateRowFill) | Free |
chart | chartType, data (categories, series), style (showLegend) | Free (basic) / Pro (combo) |
Every element also accepts an optional position object with x, y, width, and height values in inches. If omitted, PaperJSX auto-layouts elements vertically with sensible margins.
Same JSON, four output formats
The defining feature of PaperJSX is that the JSON schema is format-agnostic. The same object that produces a PPTX file can also produce a PDF, DOCX, or XLSX file — each from a separate generate() function in a format-specific package.
import { generate as toPptx } from "@paperjsx/json-to-pptx";
import { generate as toPdf } from "@paperjsx/json-to-pdf";
import { generate as toDocx } from "@paperjsx/json-to-docx";
const doc = { /* same JSON schema */ };
const [pptx, pdf, docx] = await Promise.all([
toPptx(doc),
toPdf(doc),
toDocx(doc),
]);
Without PaperJSX, the same multi-format output would require PptxGenJS for PPTX, pdfmake or PDFKit for PDF, the docx npm package for DOCX, and ExcelJS for XLSX — four separate libraries with four separate APIs, four separate data structures, and four separate sets of bugs. Public search-demand data shows developers looking for "json to pptx," "json to pdf," and "json to docx" at a combined volume of 600–1,400 searches/month, suggesting strong demand for a unified solution.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
ERR_MODULE_NOT_FOUND | Using require() instead of import | Add "type": "module" to package.json or use .mjs extension |
| PowerPoint repair dialog | Invalid element structure in JSON | Validate your JSON against the PPTX package guide. Most common cause: missing type field on an element. |
| Empty slides | elements array is empty or missing | Ensure every slide object has at least one element in its elements array |
| Charts render as blank rectangles | series.values contains non-numeric data | Ensure all values in the values array are numbers, not strings |
| Image not found | Relative path resolves incorrectly in serverless | Use absolute paths or base64-encoded images in serverless environments |
