Files
Chris Tate 07de14b686 fix(image,react-pdf): resolve Buffer/stream type errors breaking build
@resvg/resvg-js's asPng() and @react-pdf/renderer's renderToBuffer()
return Node Buffer, which no longer satisfies the declared Uint8Array
return types under the current @types/node/TypeScript resolution,
failing dts builds (and therefore the release workflow's build step).
Wrap the Buffers in zero-copy Uint8Array views.

renderToStream() was declared as returning a web ReadableStream, but
@react-pdf/renderer's renderToStream() actually resolves to a
NodeJS.ReadableStream; declare what it really returns.
2026-07-01 18:53:18 -05:00
..
2026-03-27 18:17:20 -07:00
2026-05-06 22:42:42 -05:00
2026-02-27 13:03:42 -06:00
2026-02-27 13:03:42 -06:00
2026-02-27 13:03:42 -06:00

@json-render/image

Image renderer for @json-render/core. Generate SVG and PNG images from JSON specs using Satori.

Install

npm install @json-render/core @json-render/image

For PNG output, also install the optional peer dependency:

npm install @resvg/resvg-js

Quick Start

Render a spec to SVG

import { renderToSvg } from "@json-render/image/render";
import type { Spec } from "@json-render/core";

const spec: Spec = {
  root: "frame",
  elements: {
    frame: {
      type: "Frame",
      props: { width: 1200, height: 630, backgroundColor: "#1a1a2e" },
      children: ["heading", "subtitle"],
    },
    heading: {
      type: "Heading",
      props: { text: "Hello World", level: "h1", color: "#ffffff" },
      children: [],
    },
    subtitle: {
      type: "Text",
      props: { text: "Generated from JSON", fontSize: 24, color: "#a0a0b0" },
      children: [],
    },
  },
};

const svg = await renderToSvg(spec, {
  fonts: [
    {
      name: "Inter",
      data: await fetch("https://example.com/Inter-Regular.ttf").then((r) =>
        r.arrayBuffer()
      ),
      weight: 400,
      style: "normal",
    },
  ],
});

Render to PNG

import { renderToPng } from "@json-render/image/render";

const png = await renderToPng(spec, {
  fonts: [
    {
      name: "Inter",
      data: await readFile("./Inter-Regular.ttf"),
      weight: 400,
      style: "normal",
    },
  ],
});

// Write to file
await writeFile("output.png", png);

With a custom catalog

import { defineCatalog } from "@json-render/core";
import { schema, renderToSvg } from "@json-render/image";
import { standardComponentDefinitions } from "@json-render/image/catalog";
import { z } from "zod";

const catalog = defineCatalog(schema, {
  components: {
    ...standardComponentDefinitions,
    Badge: {
      props: z.object({
        label: z.string(),
        color: z.string().nullable(),
      }),
      slots: [],
      description: "A colored badge label",
    },
  },
});

Standard Components

Root

Component Description
Frame Root image container. Defines width, height, and background. Must be the root element.

Layout

Component Description
Box Generic container with padding, margin, background, border, and flex alignment.
Row Horizontal flex layout with gap, align, justify.
Column Vertical flex layout with gap, align, justify.

Content

Component Description
Heading h1-h4 heading text with color and alignment.
Text Body text with fontSize, color, weight, style, and alignment.
Image Image from a URL with width, height, and borderRadius.

Decorative

Component Description
Divider Horizontal line separator.
Spacer Empty vertical space.

Server-Side APIs

import { renderToSvg, renderToPng } from "@json-render/image/render";

// Render to an SVG string
const svg = await renderToSvg(spec, { fonts });

// Render to a PNG buffer (requires @resvg/resvg-js)
const png = await renderToPng(spec, { fonts });

Options

Option Type Default Description
fonts SatoriOptions['fonts'] [] Font data for text rendering
width number Frame prop Override image width
height number Frame prop Override image height
registry Record<string, Component> {} Custom component overrides
includeStandard boolean true Include standard components
state Record<string, unknown> {} Initial state values

Server-Safe Import

Import schema and catalog definitions without pulling in React or Satori:

import { schema, standardComponentDefinitions } from "@json-render/image/server";

License

Apache-2.0