
Json Render Core
Define json-render schemas and catalogs so AI can stream valid JSON specs for UI or video generation.
Overview
json-render-core is an agent skill most often used in Build (also Validate, Ship) that explains @json-render/core schemas, catalogs, and AI prompt generation for JSON UI/video specs.
Install
npx skills add https://github.com/vercel-labs/json-render --skill json-render-coreWhat is this skill?
- defineSchema for spec and catalog structure with optional promptTemplate
- defineCatalog maps component names to zod props and descriptions
- SpecStream JSONL format for progressive spec building
- Zod-backed props on catalog entries such as Button label and variant
- Package focus: @json-render/core schemas, catalogs, AI prompts
- Four key concepts: Schema, Catalog, Spec, SpecStream
- Catalog example documents Button with primary/secondary variant enum
Adoption & trust: 351 installs on skills.sh; 15k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
Your agent invents arbitrary JSON for UI or video and you have no shared schema, catalog, or streaming spec format.
Who is it for?
Indie agent builders standardizing LLM output into json-render specs before React or Remotion consumers render them.
Skip if: Projects that only need hand-written JSX with no AI-generated spec contract or streaming.
When should I use this skill?
Working with @json-render/core, defining schemas, creating catalogs, or building JSON specs for UI/video generation.
What do I get? / Deliverables
You publish a typed schema and catalog with Zod props and can generate prompts and SpecStream-compatible JSON for downstream renderers.
- schema module via defineSchema
- catalog module via defineCatalog with component prop definitions
- Optional custom promptTemplate for AI spec generation
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Core library work happens in Build when you shape agent outputs into structured specs and catalogs. Agent-tooling fits defineSchema, defineCatalog, prompt templates, and SpecStream JSONL for generative UI/video pipelines.
Where it fits
Create defineCatalog entries so the agent only emits Button variants you support.
Spike SpecStream JSONL to preview incremental UI from model tokens.
Tighten schema promptTemplate before production agent traffic.
How it compares
Schema-and-catalog contract layer—not a full UI framework or Remotion scene skill by itself.
Common Questions / FAQ
Who is json-render-core for?
Builders using @json-render/core to constrain and catalog AI-generated UI or video JSON specs.
When should I use json-render-core?
In Build when defining schemas and catalogs; in Validate when prototyping AI-driven UI shapes; in Ship when hardening spec streaming for production agents.
Is json-render-core safe to install?
Review the Security Audits panel on this Prism page; treat generated specs and prompts like any untrusted model output until validated.
SKILL.md
READMESKILL.md - Json Render Core
# @json-render/core Core package for schema definition, catalog creation, and spec streaming. ## Key Concepts - **Schema**: Defines the structure of specs and catalogs (use `defineSchema`) - **Catalog**: Maps component/action names to their definitions (use `defineCatalog`) - **Spec**: JSON output from AI that conforms to the schema - **SpecStream**: JSONL streaming format for progressive spec building ## Defining a Schema ```typescript import { defineSchema } from "@json-render/core"; export const schema = defineSchema((s) => ({ spec: s.object({ // Define spec structure }), catalog: s.object({ components: s.map({ props: s.zod(), description: s.string(), }), }), }), { promptTemplate: myPromptTemplate, // Optional custom AI prompt }); ``` ## Creating a Catalog ```typescript import { defineCatalog } from "@json-render/core"; import { schema } from "./schema"; import { z } from "zod"; export const catalog = defineCatalog(schema, { components: { Button: { props: z.object({ label: z.string(), variant: z.enum(["primary", "secondary"]).nullable(), }), description: "Clickable button component", }, }, }); ``` ## Generating AI Prompts ```typescript const systemPrompt = catalog.prompt(); // Uses schema's promptTemplate const systemPrompt = catalog.prompt({ customRules: ["Rule 1", "Rule 2"] }); ``` ## SpecStream Utilities For streaming AI responses (JSONL patches): ```typescript import { createSpecStreamCompiler } from "@json-render/core"; const compiler = createSpecStreamCompiler<MySpec>(); // Process streaming chunks const { result, newPatches } = compiler.push(chunk); // Get final result const finalSpec = compiler.getResult(); ``` ## Dynamic Prop Expressions Any prop value can be a dynamic expression resolved at render time: - **`{ "$state": "/state/key" }`** - reads a value from the state model (one-way read) - **`{ "$bindState": "/path" }`** - two-way binding: reads from state and enables write-back. Use on the natural value prop (value, checked, pressed, etc.) of form components. - **`{ "$bindItem": "field" }`** - two-way binding to a repeat item field. Use inside repeat scopes. - **`{ "$cond": <condition>, "$then": <value>, "$else": <value> }`** - evaluates a visibility condition and picks a branch - **`{ "$template": "Hello, ${/user/name}!" }`** - interpolates `${/path}` references with state values - **`{ "$computed": "fnName", "args": { "key": <expression> } }`** - calls a registered function with resolved args `$cond` uses the same syntax as visibility conditions (`$state`, `eq`, `neq`, `not`, arrays for AND). `$then` and `$else` can themselves be expressions (recursive). Components do not use a `statePath` prop for two-way binding. Instead, use `{ "$bindState": "/path" }` on the natural value prop (e.g. `value`, `checked`, `pressed`). ```json { "color": { "$cond": { "$state": "/activeTab", "eq": "home" }, "$then": "#007AFF", "$else": "#8E8E93" }, "label": { "$template": "Welcome, ${/user/name}!" }, "fullName": { "$computed": "fullName", "args": { "first": { "$state": "/form/firstName" }, "last": { "$state": "/form/lastName" } } } } ``` ```typescript import { resolvePropValue, resolveElementProps } from "@json-render/core"; const resolved = resolveElementProps(element.props, { stateModel: myState }); ``` ## State Watchers Elements can declare a `watch` field (top-level, sibling of type/props/children) to trigger actions when state values change: ```json { "type": "Select", "props": { "value": { "$bindState": "/form/country" }, "options": ["US", "Canada"] }, "watch": { "/form/country": { "action": "loadCities", "pa