/multi-surface-render
Multi-surface rendering with json-render — one JSON spec produces React web, Next.js, React Native, Ink terminal UIs, PDFs, emails, Remotion videos, OG images, and 3D scenes. Covers renderer target selection, registry mapping, and platform APIs (renderToBuffer, renderToStream,
$ npx -y skills add yonatangross/orchestkit --skill multi-surface-render --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.
- You can call itInvoke it directly when you want it.
- Slash command
/multi-surface-render
Context preview
The summary Claude sees to decide when to auto-load this skill.
Multi-surface rendering with json-render — one JSON spec produces React web, Next.js, React Native, Ink terminal UIs, PDFs, emails, Remotion videos, OG images, and 3D scenes. Covers renderer target selection, registry mapping, and platform APIs (renderToBuffer, renderToStream,
SKILL.md
multi-surface-render.SKILL.mdname: multi-surface-render
compatibility: "Claude Code 2.1.220+"
description: "Multi-surface rendering with json-render — one JSON spec produces React web, Next.js, React Native, Ink terminal UIs, PDFs, emails, Remotion videos, OG images, and 3D scenes. Covers renderer target selection, registry mapping, and platform APIs (renderToBuffer, renderToStream, renderToFile). Use when generating output for several platforms or creating PDF reports, email templates, demo videos, or social images from one component spec."
tags: [json-render, multi-surface, pdf, email, remotion, video, image, react, rendering, ink, nextjs]
version: 1.1.0
author: OrchestKit
user-invocable: false
disable-model-invocation: false
complexity: medium
context: inherit
persuasion-type: reference
metadata:
category: frontend
upstream-package: "@json-render/core"
upstream-version-tested: "0.19.0"
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)
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)
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
import { renderToBuffer, renderToFile } from '@json-render/react-pdf'
import { pdfRegistry } from './registries/pdf'
// Buffer for HTTP response. PDF options are { registry?, state?, handlers? }.
// includeStandard is an EMAIL option, not a PDF one (see references/upstream-pdf.md).
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
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)
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)
// Verified 2026-07-31 against @json-render/remotion@0.19.0: the export is
// `Renderer` and its props are { spec, components }. fps and durationInFrames
// belong on Remotion's own Composition, not on this renderer.
import { Renderer } from '@json-render/remotion'
import { remotionComponents } from './registries/remotion'
export const DemoVideo = () => (
<Renderer spec={spec} components={remotionComponents} />
)Render to Terminal (Ink, 0.15+)
import { render } from 'ink'
import { Renderer } from '@json-render/ink'
import { catalog } from './catalog'
import { inkRegistry } from './registries/ink'
render(<Renderer 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+)
// createNextApp lives on the /server subpath, not the package root.
import { createNextApp } from '@json-render/next/server'
const { getPageData, generateMetadata, generateStaticParams } = createNextApp({
spec, // NextAppSpec: routes keyed by Next.js URL patterns
loaders: { getPost }, // server-side data loaders referenced by route.loader
})It does **not** scaffold a project on disk. `createNextApp` returns the server-side pieces you re-export from a catch-all route, and the page itself renders through `PageRenderer`:
// app/[[.
Read more
name: multi-surface-render compatibility: "Claude Code 2.1.220+" description: "Multi-surface rendering with json-render — one JSON spec produces React web, Next.js, React Native, Ink terminal UIs, PDFs, emails, Remotion videos, OG images, and 3D scenes. Covers renderer target selection, registry mapping, and platform APIs (renderToBuffer, renderToStream, renderToFile). Use when generating output for several platforms or creating PDF reports, email templates, demo videos, or social images from one component spec." tags: [json-render, multi-surface, pdf, email, remotion, video, image, react, rendering, ink, nextjs] version: 1.1.0 author: OrchestKit user-invocable: false disable-model-invocation: false complexity: medium context: inherit persuasion-type: reference metadata: category: frontend upstream-package: "@json-render/core" upstream-version-tested: "0.19.0"
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)
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)
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
import { renderToBuffer, renderToFile } from '@json-render/react-pdf'
import { pdfRegistry } from './registries/pdf'
// Buffer for HTTP response. PDF options are { registry?, state?, handlers? }.
// includeStandard is an EMAIL option, not a PDF one (see references/upstream-pdf.md).
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
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)
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)
// Verified 2026-07-31 against @json-render/remotion@0.19.0: the export is
// `Renderer` and its props are { spec, components }. fps and durationInFrames
// belong on Remotion's own Composition, not on this renderer.
import { Renderer } from '@json-render/remotion'
import { remotionComponents } from './registries/remotion'
export const DemoVideo = () => (
<Renderer spec={spec} components={remotionComponents} />
)Render to Terminal (Ink, 0.15+)
import { render } from 'ink'
import { Renderer } from '@json-render/ink'
import { catalog } from './catalog'
import { inkRegistry } from './registries/ink'
render(<Renderer 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+)
// createNextApp lives on the /server subpath, not the package root.
import { createNextApp } from '@json-render/next/server'
const { getPageData, generateMetadata, generateStaticParams } = createNextApp({
spec, // NextAppSpec: routes keyed by Next.js URL patterns
loaders: { getPost }, // server-side data loaders referenced by route.loader
})It does **not** scaffold a project on disk. `createNextApp` returns the server-side pieces you re-export from a catch-all route, and the page itself renders through `PageRenderer`:
// app/[[.
Showing the first part of this file.
The Complete AI Development Toolkit for Claude Code — 114 skills, 37 agents, 212 hooks. Production-ready patterns for full-stack development.
Repo: yonatangross/orchestkit
Other skills on orchestkit.
- /accessibility
Accessibility patterns for WCAG 2.2 compliance, keyboard focus management, React Aria component patterns, cognitive inclusion, native HTML-first philosophy, and user preference honoring. Use when implementing screen reader support, keyboard navigation, ARIA patterns, focus
Open skill - /agent-orchestration
Agent orchestration patterns for agentic loops, multi-agent coordination, alternative frameworks, and multi-scenario workflows. Use when building autonomous agent loops, coordinating multiple agents, evaluating CrewAI/AutoGen/Swarm, or orchestrating complex multi-step scenarios.
Open skill - /ai-ui-generation
AI-assisted UI generation patterns for json-render, v0.app, Google Stitch, Bolt Cloud, and Cursor workflows. Covers prompt engineering for component and full-stack app generation, review checklists for AI-generated code, design token injection, refactoring for design system
Open skill - /analytics
Queries local analytics across OrchestKit projects for agent usage, skill frequency, hook timing, team activity, session replay, cost estimation, and model delegation trends. Privacy-safe with hashed project IDs. Supports time-range filtering and comparative analysis. Use when
Open skill - /animation-motion-design
Animation and motion design patterns using Motion library (formerly Framer Motion) and View Transitions API. Use when implementing component animations, page transitions, micro-interactions, gesture-driven UIs, or ensuring motion accessibility with prefers-reduced-motion.
Open skill - /api-design
API contract design for REST and GraphQL, covering resource shape, URL and header versioning with deprecation windows, RFC 9457 Problem Details error handling, and OpenAPI specs. Use when specifying the wire contract an endpoint exposes, choosing a versioning scheme, or
Open skill

