Skip to content

/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,

shell
$ npx -y skills add yonatangross/orchestkit --skill multi-surface-render --agent claude-code

How 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
How auto-invocation works

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.md
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/[[.
Read more
Read it on GitHub ↗

Showing the first part of this file.

Ships withorchestkit

The Complete AI Development Toolkit for Claude Code — 114 skills, 37 agents, 212 hooks. Production-ready patterns for full-stack development.

Get the whole plugin, auto-invoked
Stats
212
Stars
0
Views
22
Forks
Active
Maintenance
TypeScript
Language
MIT
License
29m ago
Last commit
7mo ago
Created

Repo: yonatangross/orchestkit

Other skills on orchestkit.