Check the current contract and validator results before you build on this.
To generate XLSX charts from JSON, represent worksheet cells and chart series separately, bind each series to explicit A1 ranges, render the workbook, and validate the returned artifact. A native chart remains an Excel object linked to worksheet data; a PNG placed on a sheet does not. Use a new-workbook render operation for generated reports. Use a bounded transform operation when the job must preserve an existing customer workbook.
Model cells and charts explicitly
An XLSX chart depends on worksheet parts, relationships, cached values and chart XML. A loose data array hides that structure and makes validation hard. A useful model names the sheet, declares the cells and binds the chart to ranges you can check before rendering.
This shortened input follows the shape of Runstamp's checked xlsx.render example:
const workbook = {
meta: {
title: "Quarterly revenue review",
creator: "Reporting service",
},
sheets: [{
name: "Summary",
columns: [
{ width: 18 },
{ width: 14 },
{ width: 14 },
],
rows: [
{ cells: [
{ value: "Region", style: { font: { bold: true } } },
{ value: "Q2", style: { font: { bold: true } } },
{ value: "Q3", style: { font: { bold: true } } },
] },
{ cells: [{ value: "North America" }, { value: 3800 }, { value: 4200 }] },
{ cells: [{ value: "EMEA" }, { value: 2900 }, { value: 3100 }] },
{ cells: [{ value: "APAC" }, { value: 2200 }, { value: 2800 }] },
],
charts: [{
type: "bar",
title: "Revenue by region",
series: [
{
name: "'Summary'!$B$1",
categories: "'Summary'!$A$2:$A$4",
values: "'Summary'!$B$2:$B$4",
},
{
name: "'Summary'!$C$1",
categories: "'Summary'!$A$2:$A$4",
values: "'Summary'!$C$2:$C$4",
},
],
anchor: {
from: { col: 4, row: 0 },
to: { col: 12, row: 16 },
},
style: { showLegend: true, showDataLabels: false },
}],
}],
};
The series ranges are part of the contract. Validate that category and value ranges have compatible lengths and remain inside the intended sheet.
Run the qualified operation
The SDK exposes the same named operation used by the CLI, MCP server, and Managed API. Local execution keeps the workbook inside the process:
import { writeFile } from "node:fs/promises";
import { local, Runstamp } from "@runstamp/sdk";
type ArtifactBytes = { bytes: Uint8Array; mediaType: string; hash: string };
const runstamp = new Runstamp({ transport: local() });
const result = await runstamp.run<ArtifactBytes>("xlsx.render", workbook, {
operation: {
deterministic: true,
lossPolicy: "collect",
timeoutMs: 20_000,
},
});
if (!result.ok) {
throw new Error(`${result.error.code}: ${result.error.remediation}`);
}
await writeFile("quarterly-revenue.xlsx", result.value.bytes);
console.log(result.value.hash, result.losses, result.diagnostics);
The XLSX render operation page lists the current limits, declared errors, declared loss codes, execution surfaces, and hashes for the public example. Copying an older blog snippet should never replace calling describe("xlsx.render") for the complete live schema.
Native chart or embedded image?
The right representation depends on what the recipient must do.
| Recipient job | Native XLSX chart | Embedded image |
|---|---|---|
| Edit series, labels, or style in Excel | Appropriate | Not editable as a chart |
| Trace a point back to worksheet data | Appropriate | No data relationship |
| Preserve a fixed approved visual | Can drift when data or style changes | Appropriate when the image is the approved artifact |
| Render consistently outside Excel | Viewer-dependent | Image rendering is usually more predictable |
| Provide accessible meaning | Requires chart title, workbook context, and testing | Requires useful alternative text outside the pixels |
Native does not mean visually identical in every viewer. Excel, LibreOffice, and browser viewers can interpret chart defaults differently. If appearance matters, add named application renders or screenshots to the acceptance profile.
Validate the input before rendering
Catch application errors before they become OOXML defects:
- sheet names must be unique and within Excel's length constraints;
- chart series must reference an existing sheet;
- category and value ranges must be nonempty and compatible;
- chart anchors must fit the intended layout;
- dates and numbers must be typed correctly rather than formatted strings;
- formulas need cached values when downstream viewers rely on them;
- chart titles, axis units, and source dates must be part of the business input.
Treat chart text as data that needs review. A technically correct chart can still use the wrong unit, truncated period, or misleading axis.
Validate the generated workbook
After rendering, check the returned result before delivery:
- Confirm that the operation succeeded and inspect losses and diagnostics.
- Record the returned SHA-256 hash and byte length.
- Run
xlsx.validateon the produced bytes. - Reopen or parse the workbook and confirm the expected sheets, ranges, and chart relationships.
- Render the workbook in named destination applications when visual fidelity matters.
- Review business values, units, titles, and source dates.
Structural validation cannot prove that a chart communicates the intended conclusion. Application rendering cannot prove that the source values are authoritative. Keep those decisions separate.
Generate a new workbook or update an existing one?
xlsx.render is appropriate when the system owns the workbook structure and creates the report from a structured model. It is not the right default for a customer-supplied workbook containing formulas, macros, named ranges, data connections, or formatting that must be preserved.
Use xlsx.transform for a bounded change to an existing workbook. The checked workbook-update Job maps a target, reads the current value, applies a declared write, and verifies both the intended change and preservation outside the target.
That distinction prevents a common failure: rebuilding a workbook that looks similar while silently losing features the customer depends on.
Chart-library decision
ExcelJS, SheetJS, and Runstamp cover different jobs. The ExcelJS comparison explains the current decision boundary and links to the relevant operation evidence.
- Choose ExcelJS for cell-oriented authoring, formatting, formulas, and streaming workflows supported by its current API.
- Choose SheetJS for broad spreadsheet parsing and data exchange; use the maintained distribution documented by SheetJS.
- Choose Runstamp's operation contract when you need a typed multi-step document workflow, qualified
xlsx.renderorxlsx.transformbehavior, explicit losses, and the same operation name across local, CLI, MCP, and managed execution.
Do not choose from download counts or a generic feature grid. Build one representative workbook, open it in the recipient's applications, exercise the edits they will make, and inspect what the tool preserves or changes.
Release checklist
- Bind every chart series to explicit worksheet ranges.
- Keep numbers numeric and use formats for display.
- Include titles, units, period, and source date in the document model.
- Validate chart anchors against the intended printable area.
- Inspect the operation's losses and diagnostics.
- Reopen and validate the exact output bytes.
- Test named destination applications when appearance matters.
- Use
xlsx.transformrather than reconstruction for customer-owned workbooks. - Bind approval and delivery to the same artifact hash.
Start with the checked XLSX render example or use the workbook-update Job when this report is generated from an existing file on a recurring cadence.
Keep the document workflow in your product.
Pick an operation, run it on a real file, then embed review and release where your customers work.