@json-render/core
Core library for json-render. Define schemas, create catalogs, generate AI prompts, and stream specs.
Installation
npm install @json-render/core zod
Key Concepts
- Schema: Defines the structure of specs and catalogs
- Catalog: Maps component/action names to their definitions with Zod props
- Spec: JSON output from AI that conforms to the schema
- SpecStream: JSONL streaming format for progressive spec building
Quick Start
Define a Schema
import { defineSchema } from "@json-render/core";
export const schema = defineSchema((s) => ({
spec: s.object({
root: s.object({
type: s.ref("catalog.components"),
props: s.propsOf("catalog.components"),
children: s.array(s.self()),
}),
}),
catalog: s.object({
components: s.map({
props: s.zod(),
description: s.string(),
}),
actions: s.map({
description: s.string(),
}),
}),
}), {
promptTemplate: myPromptTemplate,
});
Create a Catalog
import { defineCatalog } from "@json-render/core";
import { schema } from "./schema";
import { z } from "zod";
export const catalog = defineCatalog(schema, {
components: {
Card: {
props: z.object({
title: z.string(),
subtitle: z.string().nullable(),
}),
description: "A card container with title",
},
Button: {
props: z.object({
label: z.string(),
variant: z.enum(["primary", "secondary"]).nullable(),
}),
description: "A clickable button",
},
},
actions: {
submit: { description: "Submit the form" },
cancel: { description: "Cancel and close" },
},
});
Generate AI Prompts
const systemPrompt = catalog.prompt();
const systemPrompt = catalog.prompt({
system: "You are a dashboard builder.",
customRules: [
"Always include a header",
"Use Card components for grouping",
],
});
Stream AI Responses (SpecStream)
The SpecStream format uses JSONL patches to progressively build specs:
import { createSpecStreamCompiler } from "@json-render/core";
const compiler = createSpecStreamCompiler<MySpec>();
while (streaming) {
const chunk = await reader.read();
const { result, newPatches } = compiler.push(chunk);
if (newPatches.length > 0) {
setSpec(result);
}
}
const finalSpec = compiler.getResult();
SpecStream format (each line is a JSON patch):
{"op":"set","path":"/root/type","value":"Card"}
{"op":"set","path":"/root/props","value":{"title":"Hello"}}
{"op":"set","path":"/root/children/0","value":{"type":"Button","props":{"label":"Click"}}}
Low-Level Utilities
import {
parseSpecStreamLine,
applySpecStreamPatch,
compileSpecStream,
} from "@json-render/core";
const patch = parseSpecStreamLine('{"op":"set","path":"/root","value":{}}');
const obj = {};
applySpecStreamPatch(obj, patch);
const spec = compileSpecStream<MySpec>(jsonlString);
API Reference
Schema
defineSchema(builder, options?) | Create a schema with spec/catalog structure |
SchemaBuilder | Builder with s.object(), s.array(), s.map(), etc. |
Catalog
defineCatalog(schema, data) | Create a type-safe catalog from schema |
catalog.prompt(options?) | Generate AI system prompt |
SpecStream
createSpecStreamCompiler<T>() | Create streaming compiler |
parseSpecStreamLine(line) | Parse single JSONL line |
applySpecStreamPatch(obj, patch) | Apply patch to object |
compileSpecStream<T>(jsonl) | Compile entire JSONL string |
Types
Spec | Base spec type |
Catalog | Catalog type |
SpecStreamLine | Single patch operation |
SpecStreamCompiler | Streaming compiler interface |
Custom Schemas
json-render supports completely different spec formats for different renderers:
{ root: { type: "Card", props: {...}, children: [...] } }
{ composition: {...}, tracks: [...], clips: [...] }
{ pages: [...], navigation: {...}, theme: {...} }
Each renderer defines its own schema with defineSchema() and its own prompt template.