
core
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.
- Installs
- 15.1K
- Last 24h
- +87
- Trending rank
- #2188
- Security
- Pass
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/vercel-labs/json-render --skill core- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads core automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
Security audits
Independent scans of the skill's instructions and bundled files.
SKILL.md
The instructions your agent reads when the skill is triggered.
@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 available since v0.21.0; pin exact versions and keep json-render packages on the same version. 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 installation 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({ 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_item_outside_scope, repeat_state_mismatch
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 // false
Built-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" | "merge" | "diff" |
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) |
FAQ
What does the core skill do?
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.
How do I install core?
Run npx skills add https://github.com/vercel-labs/json-render --skill core in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/core/ (personal) or .claude/skills/core/ (project).
Which AI agents can use core?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is core safe to install?
Security partners have reviewed it: Gen Agent Trust Hub passed; Socket passed; Snyk passed; Runlayer passed; ZeroLeaks passed. As with any skill, read the SKILL.md and bundled scripts before installing.
Who maintains core?
It is published by vercel-labs in vercel-labs/json-render, and it is an official skill from the maker of the technology it covers. It has 15,079 recorded installs.
More from vercel-labs/json-render
react
vercel-labs/json-render
React renderer for json-render that turns JSON specs into React components. Use when working with @json-render/react, building React UIs from JSON, creating component catalogs, or rendering AI-generated specs.
codegen
vercel-labs/json-render
Code generation utilities for json-render. Use when generating code from UI specs, building custom code exporters, traversing specs, or serializing props for @json-render/codegen.
next
vercel-labs/json-render
Next.js renderer for json-render that turns JSON specs into full Next.js applications with routes, layouts, SSR, and metadata. Use when working with @json-render/next, building Next.js apps from JSON specs, or creating AI-generated multi-page applications.
devtools
vercel-labs/json-render
Drop-in inspector panel for any json-render app. Use when the user wants to debug a generative UI, inspect the spec tree, edit state at runtime, see dispatched actions, follow stream patches live, browse a catalog, or pick DOM elements to find their spec keys. Triggers include "add devtools", "debug json-render", "inspect the spec", "why is this element not rendering", "see the state at runtime", or requests to tap streams / capture action logs for @json-render/devtools.
directives
vercel-labs/json-render
Pre-built custom directives for json-render — formatting, math, string manipulation, and i18n. Use when working with @json-render/directives, defining custom directives with defineDirective, or adding $format, $math, $concat, $count, $truncate, $pluralize, $join, or $t to specs.
mcp
vercel-labs/json-render
MCP Apps integration for json-render. Use when building MCP servers that render interactive UIs in Claude, ChatGPT, Cursor, or VS Code, or when integrating json-render with the Model Context Protocol.
Popular frontend & design skills
setup-matt-pocock-skills
mattpocock/skills
Configure this repo for the engineering skills: set up its issue tracker, triage label vocabulary, and domain doc layout. Run once before first use of the other engineering skills.
frontend-design
anthropics/skills
Guidance for distinctive, intentional visual design when building new UI or reshaping an existing one. Helps with aesthetic direction, typography, and making choices that don't read as templated defaults.
hyperframes-animation
heygen-com/hyperframes
All animation knowledge for HyperFrames — atomic motion rules, multi-phase scene blueprints, scene transitions, broader motion-design techniques, AND the seven runtime adapters (GSAP default, plus Lottie, Three.js, Anime.js, CSS keyframes, Web Animations API, TypeGPU). Use for any motion or animation task: pick 2-4 rules and compose, or load a blueprint, or look up runtime-specific API (e.g. GSAP eases / Lottie player / Three.js mixer). Also covers auditing an existing composition's choreography (animation map) and 24 named text-animation effects. HyperFrames-native: single paused timeline, seek-safe, deterministic.
prototype
mattpocock/skills
Build a throwaway prototype to answer a design question. Use when the user wants to sanity-check whether a state model or logic feels right, or explore what a UI should look like.
hyperframes-core
heygen-com/hyperframes
The HyperFrames composition contract — build one renderable project. Use for composition structure, the data- timing attributes, class="clip", tracks, sub-compositions, variables, framework-owned media playback, deterministic-render rules, and validation. Read before writing composition HTML.
vercel-react-best-practices
vercel-labs/agent-skills
React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.