Procházet zdrojové kódy

prepare v0.11.0 (#175)

* prepare v0.11.0

* release instructions
Chris Tate před 4 měsíci
rodič
revize
3f1e71e779

+ 2 - 1
.changeset/config.json

@@ -15,7 +15,8 @@
       "@json-render/redux",
       "@json-render/jotai",
       "@json-render/vue",
-      "@json-render/xstate"
+      "@json-render/xstate",
+      "@json-render/image"
     ]
   ],
   "linked": [],

+ 17 - 0
.changeset/v0-11-release.md

@@ -0,0 +1,17 @@
+---
+"@json-render/core": minor
+"@json-render/image": minor
+---
+
+Image renderer: generate SVG and PNG from JSON specs.
+
+### New: `@json-render/image` Package
+
+Server-side image renderer powered by Satori. Turns the same `{ root, elements }` spec format into SVG or PNG output for OG images, social cards, and banners.
+
+- `renderToSvg(spec, options)` — render spec to SVG string
+- `renderToPng(spec, options)` — render spec to PNG buffer (requires `@resvg/resvg-js`)
+- 9 standard components: Frame, Box, Row, Column, Heading, Text, Image, Divider, Spacer
+- `standardComponentDefinitions` catalog for AI prompt generation
+- Server-safe import path: `@json-render/image/server`
+- Sub-path exports: `/render`, `/catalog`, `/server`

+ 35 - 0
AGENTS.md

@@ -73,6 +73,41 @@ Do **not** add `--port` flags -- portless handles port assignment automatically.
   - Skills in `skills/*/SKILL.md` (if the package has a corresponding skill)
   - `AGENTS.md` (if workflow or conventions change)
 
+## Releases
+
+This monorepo uses [Changesets](https://github.com/changesets/changesets) for versioning and publishing.
+
+### Fixed version group
+
+All public `@json-render/*` packages are in a **fixed** group (see `.changeset/config.json`). A changeset that bumps any one of them bumps all of them to the same version. You only need to list the packages that actually changed in the changeset front matter — the fixed group handles the rest.
+
+### Preparing a release
+
+When asked to prepare a release (e.g. "prepare v0.12.0"):
+
+1. **Create a changeset file** at `.changeset/v0-<N>-release.md` following the existing pattern:
+   - YAML front matter listing changed packages with bump type (`minor` for feature releases, `patch` for bug-fix-only releases)
+   - A one-line summary, then `### New:` / `### Improved:` / `### Fixed:` sections describing each change
+   - Always list `@json-render/core` plus any packages with actual code changes
+2. **Do NOT bump versions** in `package.json` files — CI runs `pnpm ci:version` (which calls `changeset version`) to do that automatically
+3. **Do NOT manually write `CHANGELOG.md`** entries — `changeset version` generates them from the changeset file
+4. **Add new packages to the fixed group** in `.changeset/config.json` if they should be versioned together with the rest
+5. **Fill documentation gaps** — every public package should have:
+   - A row in the root `README.md` packages table
+   - A renderer section in the root `README.md` (if it's a renderer)
+   - An API reference page at `apps/web/app/(main)/docs/api/<name>/page.mdx`
+   - An entry in `apps/web/lib/page-titles.ts` and `apps/web/lib/docs-navigation.ts`
+   - An entry in the docs-chat system prompt (`apps/web/app/api/docs-chat/route.ts`)
+   - A skill at `skills/json-render-<name>/SKILL.md`
+   - A `packages/<name>/README.md`
+6. **Run `pnpm type-check`** after all changes to verify nothing is broken
+
+### CI scripts
+
+- `pnpm changeset` — interactively create a new changeset
+- `pnpm ci:version` — run `changeset version` + lockfile update (CI only)
+- `pnpm ci:publish` — build all packages and publish to npm (CI only)
+
 <!-- opensrc:start -->
 
 ## Source Code Reference

+ 30 - 0
README.md

@@ -118,6 +118,7 @@ function Dashboard({ spec }) {
 | `@json-render/react-native` | React Native renderer with standard mobile components |
 | `@json-render/remotion` | Remotion video renderer, timeline schema |
 | `@json-render/react-pdf` | React PDF renderer for generating PDF documents from specs |
+| `@json-render/image` | Image renderer for SVG/PNG output (OG images, social cards) via Satori |
 | `@json-render/redux` | Redux / Redux Toolkit adapter for `StateStore` |
 | `@json-render/zustand` | Zustand adapter for `StateStore` |
 | `@json-render/jotai` | Jotai adapter for `StateStore` |
@@ -287,6 +288,35 @@ const spec = {
 const buffer = await renderToBuffer(spec);
 ```
 
+### Image (SVG/PNG)
+
+```typescript
+import { renderToPng } from "@json-render/image/render";
+
+const spec = {
+  root: "frame",
+  elements: {
+    frame: {
+      type: "Frame",
+      props: { width: 1200, height: 630, backgroundColor: "#1a1a2e" },
+      children: ["heading"],
+    },
+    heading: {
+      type: "Heading",
+      props: { text: "Hello World", level: "h1", color: "#ffffff" },
+      children: [],
+    },
+  },
+};
+
+// Render to PNG (requires @resvg/resvg-js)
+const png = await renderToPng(spec, { fonts });
+
+// Or render to SVG string
+import { renderToSvg } from "@json-render/image/render";
+const svg = await renderToSvg(spec, { fonts });
+```
+
 ## Features
 
 ### Streaming (SpecStream)

+ 364 - 0
apps/web/app/(main)/docs/api/image/page.mdx

@@ -0,0 +1,364 @@
+import { pageMetadata } from "@/lib/page-metadata"
+export const metadata = pageMetadata("docs/api/image")
+
+# @json-render/image
+
+Image renderer. Turn JSON specs into SVG and PNG images using [Satori](https://github.com/vercel/satori).
+
+## Install
+
+```bash
+npm install @json-render/core @json-render/image
+```
+
+For PNG output, also install the optional peer dependency:
+
+```bash
+npm install @resvg/resvg-js
+```
+
+See the [Image example](https://github.com/vercel-labs/json-render/tree/main/examples/image) for a full working example.
+
+## schema
+
+The image element schema for image specs. Use with `defineCatalog` from core.
+
+```typescript
+import { defineCatalog } from '@json-render/core';
+import { schema, standardComponentDefinitions } from '@json-render/image';
+
+const catalog = defineCatalog(schema, {
+  components: standardComponentDefinitions,
+});
+```
+
+## Render Functions
+
+Server-side functions for producing image output. Both accept a spec and optional `RenderOptions`.
+
+```typescript
+import { renderToSvg, renderToPng } from '@json-render/image/render';
+
+const svg = await renderToSvg(spec, { fonts });
+
+const png = await renderToPng(spec, { fonts });
+await writeFile('output.png', png);
+```
+
+### RenderOptions
+
+```typescript
+interface RenderOptions {
+  registry?: ComponentRegistry;
+  includeStandard?: boolean;  // default: true
+  state?: Record<string, unknown>;
+  fonts?: SatoriOptions['fonts'];
+  width?: number;
+  height?: number;
+}
+```
+
+<table>
+  <thead>
+    <tr>
+      <th>Option</th>
+      <th>Type</th>
+      <th>Default</th>
+      <th>Description</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td><code>fonts</code></td>
+      <td><code>{"SatoriOptions['fonts']"}</code></td>
+      <td><code>[]</code></td>
+      <td>Font data for text rendering (required for meaningful output)</td>
+    </tr>
+    <tr>
+      <td><code>width</code></td>
+      <td><code>number</code></td>
+      <td>Frame prop</td>
+      <td>Override the output image width</td>
+    </tr>
+    <tr>
+      <td><code>height</code></td>
+      <td><code>number</code></td>
+      <td>Frame prop</td>
+      <td>Override the output image height</td>
+    </tr>
+    <tr>
+      <td><code>registry</code></td>
+      <td><code>{"Record<string, ComponentRenderer>"}</code></td>
+      <td><code>{"{}"}</code></td>
+      <td>Custom component map (merged with standard components)</td>
+    </tr>
+    <tr>
+      <td><code>includeStandard</code></td>
+      <td><code>boolean</code></td>
+      <td><code>true</code></td>
+      <td>Include built-in standard components</td>
+    </tr>
+    <tr>
+      <td><code>state</code></td>
+      <td><code>{"Record<string, unknown>"}</code></td>
+      <td><code>{"{}"}</code></td>
+      <td>Initial state for <code>$state</code> / <code>$cond</code> dynamic prop resolution</td>
+    </tr>
+  </tbody>
+</table>
+
+## Standard Components
+
+### Root
+
+#### Frame
+
+Root image container. Defines the output image dimensions and background. Must be the root element.
+
+```typescript
+{
+  width: number;
+  height: number;
+  backgroundColor: string | null;
+  padding: number | null;
+  display: "flex" | "none" | null;
+  flexDirection: "row" | "column" | null;
+  alignItems: "flex-start" | "center" | "flex-end" | "stretch" | null;
+  justifyContent: "flex-start" | "center" | "flex-end" | "space-between" | "space-around" | null;
+}
+```
+
+### Layout
+
+#### Box
+
+Generic container with padding, margin, background, border, and flex alignment. Supports absolute positioning.
+
+```typescript
+{
+  padding: number | null;
+  paddingTop: number | null;
+  paddingBottom: number | null;
+  paddingLeft: number | null;
+  paddingRight: number | null;
+  margin: number | null;
+  backgroundColor: string | null;
+  borderWidth: number | null;
+  borderColor: string | null;
+  borderRadius: number | null;
+  flex: number | null;
+  width: number | string | null;
+  height: number | string | null;
+  alignItems: "flex-start" | "center" | "flex-end" | "stretch" | null;
+  justifyContent: "flex-start" | "center" | "flex-end" | "space-between" | "space-around" | null;
+  flexDirection: "row" | "column" | null;
+  position: "relative" | "absolute" | null;
+  top: number | null;
+  left: number | null;
+  right: number | null;
+  bottom: number | null;
+  overflow: "visible" | "hidden" | null;
+}
+```
+
+#### Row
+
+Horizontal flex layout with optional wrapping.
+
+```typescript
+{
+  gap: number | null;
+  alignItems: "flex-start" | "center" | "flex-end" | "stretch" | null;
+  justifyContent: "flex-start" | "center" | "flex-end" | "space-between" | "space-around" | null;
+  padding: number | null;
+  flex: number | null;
+  wrap: boolean | null;
+}
+```
+
+#### Column
+
+Vertical flex layout.
+
+```typescript
+{
+  gap: number | null;
+  alignItems: "flex-start" | "center" | "flex-end" | "stretch" | null;
+  justifyContent: "flex-start" | "center" | "flex-end" | "space-between" | "space-around" | null;
+  padding: number | null;
+  flex: number | null;
+}
+```
+
+### Content
+
+#### Heading
+
+Heading text at various levels. h1 is largest, h4 is smallest.
+
+```typescript
+{
+  text: string;
+  level: "h1" | "h2" | "h3" | "h4" | null;
+  color: string | null;
+  align: "left" | "center" | "right" | null;
+  letterSpacing: number | string | null;
+  lineHeight: number | null;
+}
+```
+
+#### Text
+
+Body text with configurable size, color, weight, and alignment.
+
+```typescript
+{
+  text: string;
+  fontSize: number | null;
+  color: string | null;
+  align: "left" | "center" | "right" | null;
+  fontWeight: "normal" | "bold" | null;
+  fontStyle: "normal" | "italic" | null;
+  lineHeight: number | null;
+  letterSpacing: number | string | null;
+  textDecoration: "none" | "underline" | "line-through" | null;
+}
+```
+
+#### Image
+
+Image from a URL with optional dimensions and fit.
+
+```typescript
+{
+  src: string;
+  width: number | null;
+  height: number | null;
+  borderRadius: number | null;
+  objectFit: "contain" | "cover" | "fill" | "none" | null;
+}
+```
+
+### Decorative
+
+#### Divider
+
+Horizontal line separator.
+
+```typescript
+{
+  color: string | null;
+  thickness: number | null;
+  marginTop: number | null;
+  marginBottom: number | null;
+}
+```
+
+#### Spacer
+
+Empty vertical space.
+
+```typescript
+{
+  height: number | null;
+}
+```
+
+## Catalog Definitions
+
+Pre-built definitions for creating image catalogs:
+
+```typescript
+import { standardComponentDefinitions } from '@json-render/image/catalog';
+import { defineCatalog } from '@json-render/core';
+import { schema } from '@json-render/image';
+
+const catalog = defineCatalog(schema, {
+  components: {
+    ...standardComponentDefinitions,
+    // Add custom components
+  },
+});
+```
+
+## Server-Safe Import
+
+Import schema and catalog definitions without pulling in React or Satori:
+
+```typescript
+import { schema, standardComponentDefinitions } from '@json-render/image/server';
+```
+
+## Sub-path Exports
+
+<table>
+  <thead>
+    <tr>
+      <th>Export</th>
+      <th>Description</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td><code>@json-render/image</code></td>
+      <td>Full package: schema, renderer, components, render functions</td>
+    </tr>
+    <tr>
+      <td><code>@json-render/image/server</code></td>
+      <td>Schema and catalog definitions only (no React or Satori)</td>
+    </tr>
+    <tr>
+      <td><code>@json-render/image/catalog</code></td>
+      <td>Standard component definitions and types</td>
+    </tr>
+    <tr>
+      <td><code>@json-render/image/render</code></td>
+      <td>Server-side render functions only</td>
+    </tr>
+  </tbody>
+</table>
+
+## Types
+
+<table>
+  <thead>
+    <tr>
+      <th>Export</th>
+      <th>Description</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td><code>ImageSchema</code></td>
+      <td>Schema type for image specs</td>
+    </tr>
+    <tr>
+      <td><code>ImageSpec</code></td>
+      <td>Spec type for image output</td>
+    </tr>
+    <tr>
+      <td><code>RenderOptions</code></td>
+      <td>Options for render functions</td>
+    </tr>
+    <tr>
+      <td><code>ComponentRenderProps</code></td>
+      <td>Props passed to component render functions</td>
+    </tr>
+    <tr>
+      <td><code>ComponentRenderer</code></td>
+      <td>Component render function type</td>
+    </tr>
+    <tr>
+      <td><code>ComponentRegistry</code></td>
+      <td>Map of component names to render functions</td>
+    </tr>
+    <tr>
+      <td><code>StandardComponentDefinitions</code></td>
+      <td>Type of the standard component definitions object</td>
+    </tr>
+    <tr>
+      <td><code>StandardComponentProps{'<K>'}</code></td>
+      <td>Inferred props type for a standard component by name</td>
+    </tr>
+  </tbody>
+</table>

+ 197 - 0
apps/web/app/(main)/docs/renderers/page.mdx

@@ -0,0 +1,197 @@
+import { pageMetadata } from "@/lib/page-metadata"
+export const metadata = pageMetadata("docs/renderers")
+
+# Renderers
+
+json-render supports multiple output targets. Each renderer takes the same core concept -- a JSON spec constrained to a catalog -- and renders it natively on a different platform or into a different format.
+
+All renderers share the same workflow:
+
+1. Define a catalog with `defineCatalog`
+2. AI generates a JSON spec
+3. The renderer turns the spec into platform-native output
+
+<table>
+  <thead>
+    <tr>
+      <th>Renderer</th>
+      <th>Package</th>
+      <th>Output</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td>React</td>
+      <td><code>@json-render/react</code></td>
+      <td>React component tree</td>
+    </tr>
+    <tr>
+      <td>Vue</td>
+      <td><code>@json-render/vue</code></td>
+      <td>Vue 3 component tree</td>
+    </tr>
+    <tr>
+      <td>shadcn/ui</td>
+      <td><code>@json-render/shadcn</code></td>
+      <td>Pre-built Radix UI + Tailwind components (uses React renderer)</td>
+    </tr>
+    <tr>
+      <td>React Native</td>
+      <td><code>@json-render/react-native</code></td>
+      <td>Native mobile views</td>
+    </tr>
+    <tr>
+      <td>Image</td>
+      <td><code>@json-render/image</code></td>
+      <td>SVG / PNG (via Satori)</td>
+    </tr>
+    <tr>
+      <td>React PDF</td>
+      <td><code>@json-render/react-pdf</code></td>
+      <td>PDF documents</td>
+    </tr>
+    <tr>
+      <td>Remotion</td>
+      <td><code>@json-render/remotion</code></td>
+      <td>Video compositions</td>
+    </tr>
+  </tbody>
+</table>
+
+## React
+
+Render specs as React component trees in the browser. Supports data binding, streaming, actions, validation, visibility, and computed values.
+
+```tsx
+import { defineRegistry, Renderer } from "@json-render/react";
+import { schema } from "@json-render/react/schema";
+
+const { registry } = defineRegistry(catalog, { components });
+<Renderer spec={spec} registry={registry} />
+```
+
+Use `StateProvider`, `VisibilityProvider`, and `ActionProvider` for full interactivity. See the [@json-render/react API reference](/docs/api/react) for details.
+
+## Vue
+
+Vue 3 renderer with full feature parity with React: data binding, visibility, actions, validation, repeat scopes, and streaming.
+
+```typescript
+import { defineRegistry, Renderer } from "@json-render/vue";
+import { schema } from "@json-render/vue/schema";
+
+const { registry } = defineRegistry(catalog, {
+  components: {
+    Card: ({ props, children }) =>
+      h("div", { class: "card" }, [h("h3", null, props.title), children]),
+  },
+});
+```
+
+Uses composables (`useStateStore`, `useStateBinding`, `useActions`, etc.) instead of React hooks. See the [@json-render/vue API reference](/docs/api/vue) for details.
+
+## shadcn/ui
+
+36 pre-built components using Radix UI and Tailwind CSS. Built on top of `@json-render/react` -- no custom renderer needed.
+
+```tsx
+import { defineCatalog } from "@json-render/core";
+import { schema } from "@json-render/react/schema";
+import { defineRegistry, Renderer } from "@json-render/react";
+import { shadcnComponentDefinitions } from "@json-render/shadcn/catalog";
+import { shadcnComponents } from "@json-render/shadcn";
+
+const catalog = defineCatalog(schema, {
+  components: {
+    Card: shadcnComponentDefinitions.Card,
+    Button: shadcnComponentDefinitions.Button,
+  },
+});
+
+const { registry } = defineRegistry(catalog, {
+  components: {
+    Card: shadcnComponents.Card,
+    Button: shadcnComponents.Button,
+  },
+});
+```
+
+See the [@json-render/shadcn API reference](/docs/api/shadcn) for the full component list.
+
+## React Native
+
+Render specs as native mobile views. Includes 25+ standard components and standard action definitions.
+
+```tsx
+import { defineCatalog } from "@json-render/core";
+import { schema } from "@json-render/react-native/schema";
+import { standardComponentDefinitions, standardActionDefinitions } from "@json-render/react-native/catalog";
+import { defineRegistry, Renderer } from "@json-render/react-native";
+
+const catalog = defineCatalog(schema, {
+  components: { ...standardComponentDefinitions },
+  actions: standardActionDefinitions,
+});
+
+const { registry } = defineRegistry(catalog, { components: {} });
+<Renderer spec={spec} registry={registry} />
+```
+
+See the [@json-render/react-native API reference](/docs/api/react-native) for details.
+
+## Image
+
+Generate SVG and PNG images from JSON specs using Satori. Ideal for OG images, social cards, and banners.
+
+```typescript
+import { renderToSvg, renderToPng } from "@json-render/image/render";
+
+const svg = await renderToSvg(spec, { fonts });
+const png = await renderToPng(spec, { fonts });
+```
+
+Nine standard components: Frame, Box, Row, Column, Heading, Text, Image, Divider, Spacer. PNG output requires `@resvg/resvg-js` as an optional peer dependency.
+
+See the [@json-render/image API reference](/docs/api/image) for details.
+
+## React PDF
+
+Generate PDF documents from JSON specs using `@react-pdf/renderer`. Render to buffer, stream, or file.
+
+```typescript
+import { renderToBuffer, renderToStream, renderToFile } from "@json-render/react-pdf";
+
+const buffer = await renderToBuffer(spec);
+const stream = await renderToStream(spec);
+await renderToFile(spec, "./output.pdf");
+```
+
+Standard components include Document, Page, View, Row, Column, Heading, Text, Image, Table, List, Divider, Spacer, Link, and PageNumber.
+
+See the [@json-render/react-pdf API reference](/docs/api/react-pdf) for details.
+
+## Remotion
+
+Turn JSON timeline specs into video compositions with Remotion.
+
+```tsx
+import { Player } from "@remotion/player";
+import { Renderer } from "@json-render/remotion";
+
+<Player
+  component={Renderer}
+  inputProps={{ spec }}
+  durationInFrames={spec.composition.durationInFrames}
+  fps={spec.composition.fps}
+  compositionWidth={spec.composition.width}
+  compositionHeight={spec.composition.height}
+/>
+```
+
+Uses a timeline spec format with compositions, tracks, and clips. Includes standard components (TitleCard, TypingText, ImageSlide, etc.), transitions (fade, slide, zoom, wipe), and effects.
+
+See the [@json-render/remotion API reference](/docs/api/remotion) for details.
+
+## Custom Renderers
+
+You can build your own renderer for any output target. See the [Custom Schema & Renderer](/docs/custom-schema) guide for how to define a custom schema and wire it to your own rendering logic.

+ 1 - 1
apps/web/app/api/docs-chat/route.ts

@@ -16,7 +16,7 @@ const SYSTEM_PROMPT = `You are a helpful documentation assistant for json-render
 
 GitHub repository: https://github.com/vercel-labs/json-render
 Documentation: https://json-render.dev/docs
-npm packages: @json-render/core, @json-render/react, @json-render/remotion, @json-render/codegen
+npm packages: @json-render/core, @json-render/react, @json-render/image, @json-render/remotion, @json-render/codegen
 
 You have access to the full json-render documentation via the bash and readFile tools. The docs are available as markdown files in the /workspace/docs/ directory.
 

+ 7 - 0
apps/web/lib/docs-navigation.ts

@@ -36,6 +36,7 @@ export const docsNavigation: NavSection[] = [
   {
     title: "Rendering",
     items: [
+      { title: "Renderers", href: "/docs/renderers" },
       { title: "Registry", href: "/docs/registry" },
       { title: "Streaming", href: "/docs/streaming" },
       { title: "Generation Modes", href: "/docs/generation-modes" },
@@ -69,6 +70,11 @@ export const docsNavigation: NavSection[] = [
         href: "https://github.com/vercel-labs/json-render/tree/main/examples/remotion",
         external: true,
       },
+      {
+        title: "Image",
+        href: "https://github.com/vercel-labs/json-render/tree/main/examples/image",
+        external: true,
+      },
       {
         title: "Vue",
         href: "https://github.com/vercel-labs/json-render/tree/main/examples/vue",
@@ -106,6 +112,7 @@ export const docsNavigation: NavSection[] = [
       { title: "@json-render/react-pdf", href: "/docs/api/react-pdf" },
       { title: "@json-render/shadcn", href: "/docs/api/shadcn" },
       { title: "@json-render/react-native", href: "/docs/api/react-native" },
+      { title: "@json-render/image", href: "/docs/api/image" },
       { title: "@json-render/remotion", href: "/docs/api/remotion" },
       { title: "@json-render/vue", href: "/docs/api/vue" },
       { title: "@json-render/codegen", href: "/docs/api/codegen" },

+ 2 - 0
apps/web/lib/page-titles.ts

@@ -26,6 +26,7 @@ export const PAGE_TITLES: Record<string, string> = {
   "docs/computed-values": "Computed Values",
   "docs/visibility": "Visibility",
   "docs/watchers": "Watchers",
+  "docs/renderers": "Renderers",
   "docs/generation-modes": "Generation Modes",
   "docs/code-export": "Code Export",
   "docs/custom-schema": "Custom Schema & Renderer",
@@ -44,6 +45,7 @@ export const PAGE_TITLES: Record<string, string> = {
   "docs/api/react-pdf": "@json-render/react-pdf API",
   "docs/api/react-native": "@json-render/react-native API",
   "docs/api/codegen": "@json-render/codegen API",
+  "docs/api/image": "@json-render/image API",
   "docs/api/remotion": "@json-render/remotion API",
   "docs/api/shadcn": "@json-render/shadcn API",
 };

+ 96 - 0
skills/json-render-image/SKILL.md

@@ -0,0 +1,96 @@
+---
+name: json-render-image
+description: Image renderer for json-render that turns JSON specs into SVG and PNG images via Satori. Use when working with @json-render/image, generating OG images from JSON, creating social cards, or rendering AI-generated image specs.
+---
+
+# @json-render/image
+
+Image renderer that converts JSON specs into SVG and PNG images using Satori.
+
+## Quick Start
+
+```typescript
+import { renderToPng } 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"],
+    },
+    heading: {
+      type: "Heading",
+      props: { text: "Hello World", level: "h1", color: "#ffffff" },
+      children: [],
+    },
+  },
+};
+
+const png = await renderToPng(spec, {
+  fonts: [{ name: "Inter", data: fontData, weight: 400, style: "normal" }],
+});
+```
+
+## Using Standard Components
+
+```typescript
+import { defineCatalog } from "@json-render/core";
+import { schema, standardComponentDefinitions } from "@json-render/image";
+
+export const imageCatalog = defineCatalog(schema, {
+  components: standardComponentDefinitions,
+});
+```
+
+## Adding Custom Components
+
+```typescript
+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
+
+| Component | Category | Description |
+|-----------|----------|-------------|
+| `Frame` | Root | Root container. Defines width, height, background. Must be root. |
+| `Box` | Layout | Container with padding, margin, border, absolute positioning |
+| `Row` | Layout | Horizontal flex layout |
+| `Column` | Layout | Vertical flex layout |
+| `Heading` | Content | h1-h4 heading text |
+| `Text` | Content | Body text with full styling |
+| `Image` | Content | Image from URL |
+| `Divider` | Decorative | Horizontal line separator |
+| `Spacer` | Decorative | Empty vertical space |
+
+## Key Exports
+
+| Export | Purpose |
+|--------|---------|
+| `renderToSvg` | Render spec to SVG string |
+| `renderToPng` | Render spec to PNG buffer (requires `@resvg/resvg-js`) |
+| `schema` | Image element schema |
+| `standardComponents` | Pre-built component registry |
+| `standardComponentDefinitions` | Catalog definitions for AI prompts |
+
+## Sub-path Exports
+
+| Export | Description |
+|--------|-------------|
+| `@json-render/image` | Full package: schema, components, render functions |
+| `@json-render/image/server` | Schema and catalog definitions only (no React/Satori) |
+| `@json-render/image/catalog` | Standard component definitions and types |
+| `@json-render/image/render` | Render functions only |