json-render-core
Core utility for defining JSON schemas and catalogs for AI-generated UI or video content.
Install
mkdir -p .claude/skills/json-render-core && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/1639" && unzip -o skill.zip -d .claude/skills/json-render-core && rm skill.zipInstalls to .claude/skills/json-render-core
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Core package for defining schemas, catalogs, and AI prompt generation for json-render. Use when working with @json-render/core, defining schemas, creating catalogs, or building JSON specs for UI/video generation.Key capabilities
- →Define the structure of specs and catalogs using `defineSchema`.
- →Map component and action names to their definitions with `defineCatalog`.
- →Generate AI prompts using the schema's `promptTemplate` or custom rules.
- →Process streaming JSONL patches for progressive spec building.
- →Resolve dynamic prop expressions against a state model.
- →Trigger actions based on state value changes using element watchers.
How it works
This skill provides core functionalities for defining schemas and catalogs, generating AI prompts, and processing spec streams, enabling structured JSON output for rendering.
Inputs & outputs
When to use json-render-core
- →Define a schema for UI components
- →Create a catalog for AI video generation
- →Build JSON specs for dynamic interfaces
About this skill
@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
Experimental Decision-Model Composition
For decision-model composition, import experimental_composeSpec and experimental_createEvaluator from @json-render/core. These APIs are unreleased; use a source build until published, then pin exact versions. Experimental exports and Experimental_ types can change in any release.
- Run the Gateway evaluator server-side with
{ model: "typesafe-ai/jev", apiKey: process.env.AI_GATEWAY_API_KEY! }. A plain model identifier is required; Jev is the current example; do not import a provider constructor. - Call
experimental_composeSpec({ catalog, candidates, prompt, evaluate, initialState, signal }). It is an async generator; streamstep.specsnapshots to your existing renderer and inspectcomplete.stopReason(finish,limit,unavailable). Errors and cancellation throw; retain the last snapshot as partial UI. - New trees default to
strategy: "batch": one evaluation selects root/membership, then a second arranges the selected elements when needed. The first snapshot contains selected content in catalog order under the root's default/first slot. Resource variants share one exclusive question; repeated counts include the root. Root selection takes precedence over conflicting speculative membership for that recipe/resource. Equal sibling positions retain catalog order. Combined layouts are validated before publication; cycles or excessive depth throw.maxElementscaps batched creation (default 32). Limit-truncated selections or a missing required layout call returnlimit. Usestrategy: "sequential"for legacynext/parentadapters or sequential creation. Edits stay sequential. - Batched trace steps use
choice: "select" | "layout"and ananswersrecord. Count each trace as one evaluation, including its tokens and latency once. Custom evaluators must answer every offered question; names/choices are opaque and includeroot/select_*, thenparent_*/order_*for batches. - For follow-up edits, pass the selected version as
initialSpec. It is cloned and validated; the evaluator may add, replace, remove non-root subtrees, or move/reorder them. Unchanged IDs, bindings, and state are preserved. OptionalelementDescriptionsshares identifying descriptions without exposing raw props/state.initialStateoverrides the seed state. Seeds must be valid trees within the catalog, expression subset, and depth limit. Matching recipes consume usage/resource limits; removals/replacements release them. Replacements/moves use two evaluations (select target, then recipe/destination), each counted against the budget. Treat operation and position keys as opaque. - Supply atomic candidates with
{ id, description, element: { type, props, on?, visible? }, root?, maxUses?, resource? }. Catalog alone is insufficient: the app must supply values and binding recipes. Jev chooses elements and parent slots, never free-form text or code. It never executes actions. - Candidates are configured component instances, not page templates. Build them from current app records/operations or bind props to
initialState; offer explicit alternatives for chart types, field configurations, and layout variants. The model chooses grouping and order within those options. Name required sections in prompts; structural validity does not imply semantic completeness. - V1 supports flat Spec catalogs, named slots, literals,
$state,$bindState, and state visibility. No prebuilt children, repeat/watch, computed/template/conditional props, or custom directives. Success/error callbacks must reference allowed actions. Events must be declared in the component catalog. - Props and action params are validated against initial state without applying schema transforms/defaults. Supply valid initial values and validate/authorize action calls at runtime. Built-ins without parameter schemas get name validation only.
rootdefaults true,maxUsesdefaults one, sharedresourcevalues make alternatives mutually exclusive. Defaults: 32 evaluations (terminal calls included; no extra finish call for batches), depth eight, 10-second Gateway timeout per call. Supply an overall abort signal.- Candidate descriptions, prompt, instructions, topology, and explicit
contextare sent to the evaluator. Initial state and raw props/binding values are not sent automatically. - For custom providers implement
Experimental_CompositionEvaluator: accept{ state, questions, signal }, return{ answers: { [question]: { choice, confidence? } }, usage?: { inputTokens? } }. Only return offered criteria keys.
See packages/core/README.md and /docs/jev for app integration and source-build instructions. The web playground is an example consumer, not a dependency of the API.
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();
Form Values in Action Handlers
Use findFormValue("email", params, state) to read a direct parameter, a dotted parameter key (such as "form.email"), a matching flat state key, or a slash-delimited path (such as "/form/email") in nested state. Parameter values are literal, so emails and URLs containing dots are preserved. For action bindings that read nested state, use { $state: "/form/email" }; the resolver passes that value to the handler. A bare "email" field name does not search nested state.form.email.
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({ pro
---
*Content truncated.*
How it compares
This skill offers structured schema and catalog definitions for AI-generated JSON, providing a framework for consistent output that differs from unstructured JSON generation.
Compared to similar skills
json-render-core side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| json-render-core (this skill) | 3 | 3mo | No flags | Advanced |
| turborepo | 61 | 3mo | Review | Intermediate |
| codex-skill | 12 | 6mo | Review | Advanced |
| run-nx-generator | 5 | 4mo | Review | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by vercel-labs
View all by vercel-labs →You might also like
turborepo
vercel
Turborepo monorepo build system guidance. Triggers on: turbo.json, task pipelines, dependsOn, caching, remote cache, the "turbo" CLI, --filter, --affected, CI optimization, environment variables, internal packages, monorepo structure/best practices, and boundaries. Use when user: configures tasks/workflows/pipelines, creates packages, sets up monorepo, shares code between apps, runs changed/affected packages, debugs cache, or has apps/packages directories.
codex-skill
feiskyer
Use when user asks to leverage codex, gpt-5, or gpt-5.1 to implement something (usually implement a plan or feature designed by Claude). Provides non-interactive automation mode for hands-off task execution without approval prompts.
run-nx-generator
nrwl
Run Nx generators with prioritization for workspace-plugin generators. Use this when generating code, scaffolding new features, or automating repetitive tasks in the monorepo.
zod-4
prowler-cloud
Zod 4 schema validation patterns. Trigger: When creating or updating Zod v4 schemas for validation/parsing (forms, request payloads, adapters), including v3 -> v4 migration patterns.
copilot-sdk
github
Build agentic applications with GitHub Copilot SDK. Use when embedding AI agents in apps, creating custom tools, implementing streaming responses, managing sessions, connecting to MCP servers, or creating custom agents. Triggers on Copilot SDK, GitHub SDK, agentic app, embed Copilot, programmable agent, MCP server, custom agent.
effect-ts-expert
ojowwalker77
This skill should be used when the user is working with Effect-TS, asks to "write Effect code", "use Effect", "functional TypeScript", "handle errors with Effect", "dependency injection Effect", "Effect Layer", or needs expert-level guidance on Effect-TS patterns, error handling, concurrency, and best practices.