Skip to main content

json-render-catalog

json-render component catalog patterns for AI-safe generative UI. Define Zod-typed catalogs that constrain what AI can generate, use @json-render/shadcn for 36 pre-built components, optimize specs with YAML mode, and apply the three edit modes (patch/merge/diff) for progressive updates. Use when building AI-generated UIs, defining component catalogs, or integrating json-render into React/Vue/Svelte/React Native/Ink/Next.js projects.

Source facts

Repository
yonatangross/orchestkit
Last source activity
September 30, 2026 at 16:10
Detected SKILL.md language
English
Stars
295
Forks
35

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
12 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
json-render-catalog
compatibility
Claude Code 2.1.277+
description
json-render component catalog patterns for AI-safe generative UI. Define Zod-typed catalogs that constrain what AI can generate, use @json-render/shadcn for 36 pre-built components, optimize specs with YAML mode, and apply the three edit modes (patch/merge/diff) for progressive updates. Use when building AI-generated UIs, defining component catalogs, or integrating json-render into React/Vue/Svelte/React Native/Ink/Next.js projects.
user-invocable
false
disable-model-invocation
false
context
inherit
metadata
{"category":"frontend","upstream-package":"@json-render/core","upstream-version-tested":"0.21.0","shadcn-component-count":36,"version":"1.3.0","author":"OrchestKit","complexity":"medium","tags":"json-render, genui, zod, catalog, shadcn, ai-ui, component-catalog, vercel"}
Upstream version reference: `@json-render/core` 0.21.0 (2026-09-30). # json-render Component Catalogs json-render (Vercel Labs, 12.9K stars, Apache-2.0) is a framework for AI-safe generative UI. AI generates flat-tree JSON (or YAML) specs constrained to a developer-defined catalog — the catalog is the contract between your design system and AI output. If a component or prop is not in the catalog, AI cannot generate it. ## Storybook → catalog import (#1529, 2026-04) When the project ships a Storybook setup, **import the catalog from Storybook stories** instead of hand-writing one. The bundled importer at `scripts/storybook-to-catalog.mjs` reads a `@storybook/addon-mcp` `list-all-documentation` manifest and emits a Zod-typed `catalog.ts` plus a `components.tsx` registry. ```bash node "${CLAUDE_SKILL_DIR}/scripts/storybook-to-catalog.mjs" storybook-manifest.json \ --out src/genui/catalog.ts \ --components src/genui/components.tsx \ --project-root . ``` Storybook becomes the single source of truth — adding a story automatically expands the AI-allowed surface; removing one shrinks it. AI safety is enforced at import: callbacks, raw object props, and `z.any()` are dropped. Full mapping: `references/storybook-import.md`. Companion fixture for testing: `references/storybook-fixture.json`. ## New in 2026-04 → 2026-08 (json-render 0.14 → 0.20) - **Named slots (0.20)** — `UIElement` gains `slots?: Record<string, string[]>`, a catalog declares which it accepts via `slots: ["default", "header", "footer"]`, and a React registry component receives named content as `slots?.header` while `children` stays the default slot. Prefer this over encoding layout regions as separate sibling elements. - **Nested repeats and item-scoped visibility (0.20)** — `repeat.statePath` accepts an item-relative `{"$item": "employees"}` form (new `RepeatStatePath` type plus `resolveRepeatStatePath` / `resolveRepeatItemStatePath`), and a `repeat` combined with `visible: {"$item": "status", "eq": "todo"}` on the same element now filters items instead of erroring. `validateSpec` gains `invalid_visible`, `repeat_without_children`, `repeat_item_outside_scope` and `repeat_state_mismatch` issue codes. - **Custom directives API (0.19)** — `@json-render/core` now ships `defineDirective`, letting you declare new JSON shapes (e.g. `$format`, `$math`) that resolve to computed values at render time. Directives compose by nesting and resolve inside-out. All four renderers (React, Vue, Svelte, Solid) have built-in directive resolution. This is the safe escape hatch for computed values without widening the catalog to `z.any()`. - **`@json-render/directives` package (0.19)** — seven ready-made directives: `$format` (date / currency / number / percent via `Intl`), `$math` (add, subtract, multiply, divide, mod, min, max, round, floor, ceil, abs), `$concat`, `$count`, `$truncate`, `$pluralize`, `$join`. Plus `createI18nDirective` for `$t` translation keys with `{{param}}` interpolation, and `standardDirectives` for one-line registration. Register once, use in any spec — AI no longer needs string-mangling or duplicated literals. - **Devtools ecosystem (0.18)** — five new packages: `@json-render/devtools` core + framework adapters for React, Vue, Svelte, Solid. Inspector panel has six tabs (Spec, State, Actions, Stream, Catalog, Pick) with DOM element picking that maps back to spec keys. Tree-shakes to `null` in production. Companion Next.js demo app shipped with AI-chat + catalog integration. Action observer infrastructure exposed for adapters to mirror events into the panel. - **Zod 4 fix (0.18)** — `formatZodType` now correctly handles `z.record()`, `z.default()`, and `z.literal()` (previously produced empty/wrong prompt output). - **Three edit modes (0.14)** — `patch` (RFC 6902), `merge` (RFC 7396), `diff` (unified) for progressive AI refinements. `buildEditUserPrompt()` + `diffToPatches()` + `deepMergeSpec()` in `@json-render/core`. - **`@json-render/yaml` (0.14)** — official YAML wire format + streaming parser; `buildUserPrompt({ format: 'yaml' })`. - **`@json-render/ink` (0.15)** — render catalogs to terminal UIs (Ink-based, 20+ components) using the same spec. - **`@json-render/next` (0.16)** — generate full Next.js apps (routes, layouts, SSR, metadata) from a single spec. - **`@json-render/shadcn-svelte` (0.16)** — 36-component Svelte 5 + Tailwind mirror of the React shadcn catalog. - **shadcn catalog at 36 components** (was documented as 29 — the count was wrong even at 0.13). Use `@json-render/shadcn` as-is, or spread `shadcnComponentDefinitions` together with your own definitions. - **`@json-render/react-three-fiber`** ships 19 components (verified 2026-07-31 against the upstream skill; do not restate the roster here, see Upstream coverage). - **`@json-render/mcp`** — upgrade plain MCP tool JSON into interactive iframes inside Claude/Cursor/ChatGPT conversations. See the `ork:mcp-visual-output` skill. - **MCP multi-surface**: same spec renders to React, PDF (`@json-render/react-pdf`), email (`@json-render/react-email`), terminal (Ink), Next.js apps, and Remotion videos. ## Directives — @json-render/directives (0.19) Directives are the safe escape hatch for computed values. AI emits a `$`-prefixed object, the renderer resolves it inside-out before the component receives props — the catalog stays strict (no `z.any()` widening) and the spec stays declarative. The `@json-render/directives` package ships seven prebuilt directives plus an i18n factory; `standardDirectives` exports them as one array for one-line registration. Directives nest freely (e.g. `$format` wrapping `$math`) and are resolved by all four renderer integrations (React, Vue, Svelte, Solid). ### Registration ```tsx import { defineRegistry, JSONUIProvider, Renderer } from '@json-render/react' import { standardDirectives, createI18nDirective } from '@json-render/directives' const directives = [ ...standardDirectives, createI18nDirective({ locale: 'en', fallbackLocale: 'en', messages: { en: { greeting: 'Hello, {{name}}!' } }, }), ] const { registry } = defineRegistry(catalog, { components }) // directives register on the provider (RendererProps has no directives prop) <JSONUIProvider registry={registry} directives={directives}> <Renderer spec={spec} registry={registry} /> </JSONUIProvider> ``` ### The seven prebuilt directives | Directive | Purpose | Minimal usage | |-----------|---------|---------------| | `$format` | `Intl`-based formatting for `date`, `currency`, `number`, `percent`. Supports `locale`, `currency`, `notation`, and `style: "relative"` for human-readable date deltas. | `{ "$format": "currency", "value": 1299, "currency": "USD" }` → `$1,299.00` | | `$math` | Arithmetic — `add`, `subtract`, `multiply`, `divide`, `mod`, `min`, `max`, `round`, `floor`, `ceil`, `abs`. Division by zero returns `0`; non-numeric inputs coerce to `0`. | `{ "$math": "multiply", "a": { "$state": "/qty" }, "b": 9.99 }` | | `$concat` | Joins an array of dynamic values into a string, resolving each element through the directive pipeline first. | `{ "$concat": ["Hello, ", { "$state": "/user/name" }, "!"] }` | | `$count` | Length of an array or string; `0` for anything else. | `{ "$count": { "$state": "/items" } }` | | `$truncate` | Truncate to `length` (default 100) with optional `suffix` (default `...`). No-op if already short enough. | `{ "$truncate": { "$state": "/bio" }, "length": 80 }` | | `$pluralize` | Singular/plural/zero selection. Prepends the count automatically (`"3 items"`, `"1 item"`, or the literal `zero` form). | `{ "$pluralize": { "$state": "/cart/count" }, "zero": "no items", "one": "item", "other": "items" }` | | `$join` | Join an array with a `separator` (default `", "`). | `{ "$join": { "$state": "/tags" }, "separator": " · " }` | `createI18nDirective({ locale, messages, fallbackLocale? })` registers a `$t` directive with `{{param}}` interpolation: `{ "$t": "greeting", "params": { "name": "Ada" } }`. ### Custom directives via `defineDirective` `defineDirective` lives in `@json-render/core` (0.19+). A directive declares a Zod schema for its JSON shape and a `resolve(raw, ctx)` function — use `resolvePropValue(raw.field, ctx)` to recursively resolve any nested directive or state reference before computing. ```ts import { defineDirective, resolvePropValue } from '@json-render/core' import { z } from 'zod' export const initialsDirective = defineDirective({ name: '$initials', description: 'First letter of each word, uppercased.', schema: z.object({ $initials: z.unknown() }), resolve(raw, ctx) { const text = String(resolvePropValue(raw.$initials, ctx) ?? '') return text.split(/\s+/).map((w) => w[0]?.toUpperCase() ?? '').join('') }, }) ``` Spread into the renderer alongside `standardDirectives`: `directives={[...standardDirectives, initialsDirective]}`. Keep the schema tight — directives are the only place where AI gets to emit non-catalog JSON, so let Zod enforce shape just like a component prop. ## Upstream coverage (do not restate) json-render ships its own per-package skills. This skill wraps them and keeps only the delta: our Storybook import path, our catalog constraints, and the scars in `references/ork-delta.md`. Do not copy vendor rosters or API tables back in. | Topic | First-party source | |-------|--------------------| | Core API (`defineSchema`, `defineCatalog`, prompts, spec streaming, validation, `StateStore`) | https://github.com/vercel-labs/json-render `skills/core/SKILL.md` | | Spec format, dynamic prop expressions (`$state` / `$bindState` / `$cond` / `$template` / `$computed`), `watch`, visibility | https://github.com/vercel-labs/json-render `skills/core/SKILL.md` and `skills/react/SKILL.md` | | Built-in actions (`setState`, `pushState`, `removeState`, `validateForm`) and the event system | https://github.com/vercel-labs/json-render `skills/react/SKILL.md` | | shadcn component roster and prop schemas (36 components, React and Svelte) | `vercel:shadcn` skill, plus https://github.com/vercel-labs/json-render `skills/shadcn/SKILL.md` and `skills/shadcn-svelte/SKILL.md` | | Per-renderer components and APIs (Vue, Svelte, Solid, React Native, Ink, Next.js, PDF, email, image, Remotion, react-three-fiber) | https://github.com/vercel-labs/json-render `skills/<package>/SKILL.md` | | YAML wire format, fences, streaming compiler, edit modes | https://github.com/vercel-labs/json-render `skills/yaml/SKILL.md` | | State adapters (`zustandStateStore`, `reduxStateStore`, `jotaiStateStore`, `xstateStoreStateStore`) | https://github.com/vercel-labs/json-render `skills/zustand/SKILL.md`, `skills/redux/SKILL.md`, `skills/jotai/SKILL.md`, `skills/xstate/SKILL.md` | | MCP Apps integration (`createMcpApp`, iframe client) | `ork:mcp-visual-output`, plus https://github.com/vercel-labs/json-render `skills/mcp/SKILL.md` | | Migrating a hand-rolled JSON-to-component mapper | https://github.com/vercel-labs/json-render `skills/core/SKILL.md` (catalog + spec contract is the target shape) | Our delta, the part no upstream doc carries: `references/ork-delta.md`. ## Quick Reference | Category | Rules | Impact | When to Use | |----------|-------|--------|-------------| | [Catalog Definition](#catalog-definition) | 1 | HIGH | Defining component catalogs with Zod | | [Prop Constraints](#prop-constraints) | 1 | HIGH | Constraining AI-generated props for safety | | [Token Optimization](#token-optimization) | 1 | MEDIUM | Reducing token usage with YAML mode | **Total: 3 rules across 3 categories** ## How json-render Works 1. **Developer defines a catalog** — Zod-typed component definitions with constrained props 2. **AI generates a spec** — flat-tree JSON/YAML referencing only catalog components 3. **Runtime renders the spec** — `<Renderer>` component validates and renders each element The catalog is the safety boundary. AI can only reference types that exist in the catalog, and props are validated against Zod schemas at runtime. This prevents hallucinated components and invalid props from reaching the UI. ## Quick Start — 3 Steps ### Step 1: Define a Catalog ```typescript import { defineCatalog } from '@json-render/core'
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub