
Core
- 3.8k installs
- 15.8k repo stars
- Updated July 8, 2026
- vercel-labs/json-render
core is an agent skill for @json-render/core that defines schemas and catalogs and generates AI prompts for structured JSON UI and video specs.
About
core is the foundational json-render skill for working with @json-render/core when defining schemas, catalogs, and AI prompt generation for structured UI and video specs. Schemas use defineSchema to declare spec and catalog object shapes with optional promptTemplate hooks for custom LLM instructions. Catalogs map component and action names via defineCatalog with Zod-validated props and human-readable descriptions that agents must conform to when emitting Spec JSON. SpecStream provides JSONL streaming utilities through createSpecStreamCompiler for progressive spec building as partial patches arrive from models. Dynamic prop expressions support $state reads, $bindState two-way binding, $bindItem repeat scopes, $cond branches, and $template string interpolation resolved at render time. catalog.prompt() generates system prompts from the schema template with optional customRules arrays. Developers reach for core when bootstrapping a json-render pipeline, extending a component catalog, debugging invalid agent-generated specs, or wiring streaming compilers before specs hit a renderer.
- defineSchema declares spec and catalog structures with optional promptTemplate for custom AI prompts.
- defineCatalog maps component and action names with Zod props and descriptions for type-safe agent output.
- createSpecStreamCompiler processes JSONL streaming chunks into progressive spec patches and final results.
- Dynamic expressions include $state, $bindState, $bindItem, $cond, and $template interpolation forms.
- catalog.prompt() emits system prompts from the schema with optional customRules overrides.
Core by the numbers
- 3,810 all-time installs (skills.sh)
- +399 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #194 of 16,659 AI & Agent Building skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
core capabilities & compatibility
- Capabilities
- schema definition with defineschema · zod validated catalog mapping with definecatalog · specstream jsonl progressive compilation · dynamic prop expression resolution patterns
- Use cases
- orchestration · frontend
What core says it does
Core package for schema definition, catalog creation, and spec streaming.
const systemPrompt = catalog.prompt();
npx skills add https://github.com/vercel-labs/json-render --skill coreAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3.8k |
|---|---|
| repo stars | ★ 15.8k |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 8, 2026 |
| Repository | vercel-labs/json-render ↗ |
How do I define json-render schemas and catalogs so LLM output stays type-safe and renderable as progressive SpecStream JSON?
Define schemas, catalogs, and structured JSON specs that AI agents can reliably generate for UI and video rendering pipelines.
Who is it for?
Developers building json-render pipelines who need defineSchema, defineCatalog, and SpecStream streaming for agent-generated UI specs.
Skip if: Skip when the project does not use @json-render/core or when you only need hand-written React components without AI spec generation.
When should I use this skill?
User works with @json-render/core, defineSchema, defineCatalog, SpecStream, or catalog.prompt for AI-generated specs.
What you get
A schema, Zod-validated catalog, generated system prompt, and optional SpecStream compiler for streaming agent spec patches.
- Schema definition
- Component catalog
- Spec JSON and SpecStream output
Files
@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
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
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
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):
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).
{
"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" }
}
}
}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:
{
"type": "Select",
"props": { "value": { "$bindState": "/form/country" }, "options": ["US", "Canada"] },
"watch": {
"/form/country": { "action": "loadCities", "params": { "country": { "$state": "/form/country" } } }
},
"children": []
}Watchers only fire on value changes, not on initial render.
Validation
Built-in validation functions: required, email, url, numeric, minLength, maxLength, min, max, pattern, matches, equalTo, lessThan, greaterThan, requiredIf.
Cross-field validation uses $state expressions in args:
import { check } from "@json-render/core";
check.required("Field is required");
check.matches("/form/password", "Passwords must match");
check.lessThan("/form/endDate", "Must be before end date");
check.greaterThan("/form/startDate", "Must be after start date");
check.requiredIf("/form/enableNotifications", "Required when enabled");User Prompt Builder
Build structured user prompts with optional spec refinement and state context:
import { buildUserPrompt } from "@json-render/core";
// Fresh generation
buildUserPrompt({ prompt: "create a todo app" });
// Refinement with edit modes (default: patch-only)
buildUserPrompt({ prompt: "add a toggle", currentSpec: spec, editModes: ["patch", "merge"] });
// With runtime state
buildUserPrompt({ prompt: "show data", state: { todos: [] } });Available edit modes: "patch" (RFC 6902 JSON Patch), "merge" (RFC 7396 Merge Patch), "diff" (unified diff).
Spec Validation
Validate spec structure and auto-fix common issues:
import { validateSpec, autoFixSpec } from "@json-render/core";
const { valid, issues } = validateSpec(spec);
// issues include: missing_child, invalid_visible (malformed conditions),
// repeat_without_children, repeat_state_mismatch (statePath not an array in state)
const { spec: fixed, fixDetails } = autoFixSpec(spec);
// fixDetails entries are { message, lossy }. Lossless fixes relocate
// misplaced fields; lossy fixes prune dangling children references.
// In a repair loop, withhold lossy fixes until retries are exhausted:
const attempt = autoFixSpec(spec, { lossy: retriesExhausted });Visibility Conditions
Control element visibility with state-based conditions. VisibilityContext is { stateModel: StateModel }.
import { visibility } from "@json-render/core";
// Syntax
{ "$state": "/path" } // truthiness
{ "$state": "/path", "not": true } // falsy
{ "$state": "/path", "eq": value } // equality
[ cond1, cond2 ] // implicit AND
// Helpers
visibility.when("/path") // { $state: "/path" }
visibility.unless("/path") // { $state: "/path", not: true }
visibility.eq("/path", val) // { $state: "/path", eq: val }
visibility.and(cond1, cond2) // { $and: [cond1, cond2] }
visibility.or(cond1, cond2) // { $or: [cond1, cond2] }
visibility.always // true
visibility.never // falseBuilt-in Actions in Schema
Schemas can declare builtInActions -- actions that are always available at runtime and auto-injected into prompts:
const schema = defineSchema(builder, {
builtInActions: [
{ name: "setState", description: "Update a value in the state model" },
],
});These appear in prompts as [built-in] and don't require handlers in defineRegistry.
StateStore
The StateStore interface allows external state management libraries (Redux, Zustand, XState, etc.) to be plugged into json-render renderers. The createStateStore factory creates a simple in-memory implementation:
import { createStateStore, type StateStore } from "@json-render/core";
const store = createStateStore({ count: 0 });
store.get("/count"); // 0
store.set("/count", 1); // updates and notifies subscribers
store.update({ "/a": 1, "/b": 2 }); // batch update
store.subscribe(() => {
console.log(store.getSnapshot()); // { count: 1 }
});The StateStore interface: get(path), set(path, value), update(updates), getSnapshot(), subscribe(listener).
Key Exports
| Export | Purpose |
|---|---|
defineSchema | Create a new schema |
defineCatalog | Create a catalog from schema |
createStateStore | Create a framework-agnostic in-memory StateStore |
resolvePropValue | Resolve a single prop expression against data |
resolveElementProps | Resolve all prop expressions in an element |
buildUserPrompt | Build user prompts with refinement and state context |
buildEditUserPrompt | Build user prompt for editing existing specs |
buildEditInstructions | Generate prompt section for available edit modes |
isNonEmptySpec | Check if spec has root and at least one element |
deepMergeSpec | RFC 7396 deep merge (null deletes, arrays replace, objects recurse) |
diffToPatches | Generate RFC 6902 JSON Patch operations from object diff |
EditMode | Type: `"patch" \ |
validateSpec | Validate spec structure |
autoFixSpec | Auto-fix common spec issues; classifies fixes lossy/lossless, { lossy: false } withholds pruning |
createSpecStreamCompiler | Stream JSONL patches into spec |
createJsonRenderTransform | TransformStream separating text from JSONL in mixed streams |
parseSpecStreamLine | Parse single JSONL line |
applySpecStreamPatch | Apply patch to object |
StateStore | Interface for plugging in external state management |
ComputedFunction | Function signature for $computed expressions |
check | TypeScript helpers for creating validation checks |
BuiltInAction | Type for built-in action definitions (name + description) |
ActionBinding | Action binding type (includes preventDefault field) |
Related skills
How it compares
Pick core over ad-hoc JSON schema skills when the target runtime is @json-render/core with catalogs and SpecStream consumers.
FAQ
What is SpecStream in json-render core?
A JSONL streaming format processed by createSpecStreamCompiler to build specs progressively from partial agent patches.
How do catalogs generate AI prompts?
catalog.prompt() uses the schema promptTemplate and accepts optional customRules arrays for extra generation constraints.
Is Core safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.