Claude
Skills
Sign in
Back

multi-surface-render

Included with Lifetime
$97 forever

Multi-surface rendering with json-render — same JSON spec produces React web, Next.js apps, React Native, Ink terminal UIs, PDFs, emails, Remotion videos, OG images, and 3D scenes. Covers renderer target selection, registry mapping, and platform-specific APIs (renderToBuffer, renderToStream, renderToFile). Use when generating output for multiple platforms, creating PDF reports, email templates, demo videos, or social media images from a single component spec.

Designjson-rendermulti-surfacepdfemailremotionvideoimagereact

What this skill does


# Multi-Surface Rendering with json-render

Define once, render everywhere. A single json-render catalog and spec can produce React web UIs, PDF reports, HTML emails, Remotion demo videos, and OG images — each surface gets its own registry that maps catalog types to platform-native components.

## Quick Reference

| Category | Rules | Impact | When to Use |
|----------|-------|--------|-------------|
| [Target Selection](#target-selection) | 1 | HIGH | Choosing which renderer for your use case |
| [React Renderer](#react-renderer) | 1 | MEDIUM | Web apps, SPAs, dashboards |
| [PDF & Email Renderer](#pdf--email-renderer) | 1 | HIGH | Reports, documents, notifications |
| [Video & Image Renderer](#video--image-renderer) | 1 | MEDIUM | Demo videos, OG images, social cards |
| [Registry Mapping](#registry-mapping) | 1 | HIGH | Platform-specific component implementations |

**Total: 5 rules across 5 categories**

## How Multi-Surface Rendering Works

1. **One catalog** — Zod-typed component definitions shared across all surfaces
2. **One spec** — flat-tree JSON/YAML describing the UI structure
3. **Many registries** — each surface maps catalog types to its own component implementations
4. **Many renderers** — each package renders the spec using its registry

The catalog is the contract. The spec is the data. The registry is the platform-specific implementation.

## Quick Start — Same Catalog, Different Renderers

### Shared Catalog (used by all surfaces)

```typescript
import { defineCatalog } from '@json-render/core'
import { schema } from '@json-render/react/schema'
import { z } from 'zod'

export const catalog = defineCatalog(schema, {
  components: {
    Heading: {
      props: z.object({
        text: z.string(),
        level: z.enum(['h1', 'h2', 'h3']),
      }),
      children: false,
    },
    Paragraph: {
      props: z.object({ text: z.string() }),
      children: false,
    },
    StatCard: {
      props: z.object({
        label: z.string(),
        value: z.string(),
        trend: z.enum(['up', 'down', 'flat']).optional(),
      }),
      children: false,
    },
  },
})
```

### Render to Web (React)

```tsx
import { Renderer } from '@json-render/react'
import { webRegistry } from './registries/web'

// webRegistry comes from `defineRegistry(catalog, { components })`.
// RendererProps is { spec, registry, loading?, fallback? } — no catalog prop.
export const Dashboard = ({ spec }) => (
  <Renderer spec={spec} registry={webRegistry} />
)
```

### Render to PDF

```typescript
import { renderToBuffer, renderToFile } from '@json-render/react-pdf'
import { pdfRegistry } from './registries/pdf'

// Buffer for HTTP response — options are { registry, includeStandard?, state? }
const buffer = await renderToBuffer(spec, { registry: pdfRegistry })

// Direct file output — renderToFile(spec, filePath, options?)
await renderToFile(spec, './output/report.pdf', { registry: pdfRegistry })
```

### Render to Email

```typescript
import { renderToHtml } from '@json-render/react-email'
import { emailRegistry } from './registries/email'

const html = await renderToHtml(spec, { registry: emailRegistry })
await sendEmail({ to: user.email, subject: 'Weekly Report', html })
```

### Render to OG Image (Satori)

```typescript
import { renderToSvg, renderToPng } from '@json-render/image'
import { imageRegistry } from './registries/image'

const png = await renderToPng(spec, {
  registry: imageRegistry,
  width: 1200,
  height: 630,
})
```

### Render to Video (Remotion)

```tsx
import { JsonRenderComposition } from '@json-render/remotion'
import { catalog } from './catalog'
import { remotionRegistry } from './registries/remotion'

export const DemoVideo = () => (
  <JsonRenderComposition
    spec={spec}
    catalog={catalog}
    registry={remotionRegistry}
    fps={30}
    durationInFrames={150}
  />
)
```

### Render to Terminal (Ink, 0.15+)

```tsx
import { render } from 'ink'
import { InkRenderer } from '@json-render/ink'
import { catalog } from './catalog'
import { inkRegistry } from './registries/ink'

render(<InkRenderer spec={spec} catalog={catalog} registry={inkRegistry} />)
```

Useful for `/ork:*` CLI dashboards and streaming agent chat interfaces — ships 20+ Ink-native components (Box, Text, Spinner, Table, Markdown, Progress, etc.).

### Render to Next.js App (0.16+)

```typescript
import { generateNextApp } from '@json-render/next'

await generateNextApp(spec, {
  catalog,
  registry: webRegistry,
  outDir: './out',
  // generates routes, layouts, SSR handlers, and metadata
})
```

Output is a full Next.js App Router project — specs describe route trees, not just components.

## Decision Matrix — When to Use Each Target

| Target | Package | When to Use | Output |
|--------|---------|-------------|--------|
| React | `@json-render/react` | Web apps, SPAs | JSX |
| Next.js | `@json-render/next` *(0.16+)* | Full apps: routes, layouts, SSR, metadata | Next.js app |
| Vue | `@json-render/vue` | Vue projects | Vue components |
| Svelte | `@json-render/svelte` | Svelte projects | Svelte components |
| Svelte+shadcn | `@json-render/shadcn-svelte` *(0.16+)* | 36-component Svelte 5 catalog | Svelte + Tailwind |
| React Native | `@json-render/react-native` | Mobile apps (25+ components) | Native views |
| Terminal | `@json-render/ink` *(0.15+)* | CLI UIs, TUIs, streaming chat | Ink (terminal) |
| PDF | `@json-render/react-pdf` | Reports, documents | PDF buffer/file |
| Email | `@json-render/react-email` | Notifications, digests | HTML string |
| Remotion | `@json-render/remotion` | Demo videos, marketing | MP4/WebM |
| Image | `@json-render/image` | OG images, social cards | SVG/PNG (Satori) |
| YAML | `@json-render/yaml` *(0.14+)* | Token optimization, streaming parser | YAML string |
| MCP | `@json-render/mcp` | Claude/Cursor/ChatGPT conversations | Sandboxed iframe |
| 3D | `@json-render/react-three-fiber` | 3D scenes (20 components, incl. `GaussianSplat` in 0.17) | Three.js canvas |
| Codegen | `@json-render/codegen` | Source code from specs | TypeScript/JSX |

Load `rules/target-selection.md` for detailed selection criteria and trade-offs.

## PDF Renderer — Reports and Documents

The `@json-render/react-pdf` package renders specs to PDF using react-pdf under the hood. Three output modes: buffer, file, and stream.

```typescript
import { renderToBuffer, renderToFile, renderToStream } from '@json-render/react-pdf'

// In-memory buffer (for HTTP responses, S3 upload)
// options are { registry, includeStandard?, state? } — no catalog field
const buffer = await renderToBuffer(spec, { registry: pdfRegistry })
res.setHeader('Content-Type', 'application/pdf')
res.send(buffer)

// Direct file write — renderToFile(spec, filePath, options?)
await renderToFile(spec, './output/report.pdf', { registry: pdfRegistry })

// Streaming (for large documents)
const stream = await renderToStream(spec, { registry: pdfRegistry })
stream.pipe(res)
```

Load `rules/pdf-email-renderer.md` for PDF registry patterns and email rendering.

## Image Renderer — OG Images and Social Cards

The `@json-render/image` package uses Satori to convert specs to SVG, then optionally to PNG. Designed for server-side generation of social media images.

```typescript
import { renderToSvg, renderToPng } from '@json-render/image'

// SVG output (smaller, scalable)
const svg = await renderToSvg(spec, {
  registry: imageRegistry,
  width: 1200,
  height: 630,
})

// PNG output (universal compatibility)
const png = await renderToPng(spec, {
  registry: imageRegistry,
  width: 1200,
  height: 630,
})
```

Load `rules/video-image-renderer.md` for Satori constraints and Remotion composition patterns.

## Registry Mapping — Same Catalog, Platform-Specific Components

Each surface needs its own registry. The registry maps catalog types to platform-specific component implementations while the catalog and spec stay identical.

```typescript
// Web registry — uses HTML elements
const webRegistry = {
  Heading: 

Related in Design