
Json Render React
Render AI-generated or stored JSON UI specs into type-safe React trees using @json-render/react catalogs and registries.
Install
npx skills add https://github.com/vercel-labs/json-render --skill json-render-reactWhat is this skill?
- defineRegistry + Renderer pattern maps JSON specs to React component trees
- defineCatalog pairs Zod prop schemas with human-readable component descriptions for agents
- Type-safe props on catalog components (Button variants, Card titles, nullable enums)
- Designed for AI-generated specs constrained by a fixed component vocabulary
- Quick start imports from @json-render/react with a project-local catalog module
Adoption & trust: 404 installs on skills.sh; 15k GitHub stars; 3/3 security scanners passed (skills.sh audits).
Recommended Skills
Journey fit
JSON Render React belongs in Build frontend because it implements the renderer and component catalog—the layer users see—not the LLM prompt that produced the JSON. Frontend is correct for defineCatalog, defineRegistry, and Renderer wiring that turns declarative specs into buttons, cards, and nested layouts in React.
Common Questions / FAQ
Is Json Render React safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.
SKILL.md
READMESKILL.md - Json Render React
# @json-render/react React renderer that converts JSON specs into React component trees. ## Quick Start ```typescript import { defineRegistry, Renderer } from "@json-render/react"; import { catalog } from "./catalog"; const { registry } = defineRegistry(catalog, { components: { Card: ({ props, children }) => <div>{props.title}{children}</div>, }, }); function App({ spec }) { return <Renderer spec={spec} registry={registry} />; } ``` ## Creating a Catalog ```typescript import { defineCatalog } from "@json-render/core"; import { schema } from "@json-render/react/schema"; import { defineRegistry } from "@json-render/react"; import { z } from "zod"; // Create catalog with props schemas export const catalog = defineCatalog(schema, { components: { Button: { props: z.object({ label: z.string(), variant: z.enum(["primary", "secondary"]).nullable(), }), description: "Clickable button", }, Card: { props: z.object({ title: z.string() }), description: "Card container with title", }, }, }); // Define component implementations with type-safe props const { registry } = defineRegistry(catalog, { components: { Button: ({ props }) => ( <button className={props.variant}>{props.label}</button> ), Card: ({ props, children }) => ( <div className="card"> <h2>{props.title}</h2> {children} </div> ), }, }); ``` ## Spec Structure (Element Tree) The React schema uses an element tree format: ```json { "root": { "type": "Card", "props": { "title": "Hello" }, "children": [ { "type": "Button", "props": { "label": "Click me" } } ] } } ``` ## Visibility Conditions Use `visible` on elements to show/hide based on state. New syntax: `{ "$state": "/path" }`, `{ "$state": "/path", "eq": value }`, `{ "$state": "/path", "not": true }`, `{ "$and": [cond1, cond2] }` for AND, `{ "$or": [cond1, cond2] }` for OR. Helpers: `visibility.when("/path")`, `visibility.unless("/path")`, `visibility.eq("/path", val)`, `visibility.and(cond1, cond2)`, `visibility.or(cond1, cond2)`. ## Providers | Provider | Purpose | |----------|---------| | `StateProvider` | Share state across components (JSON Pointer paths). Accepts optional `store` prop for controlled mode. | | `ActionProvider` | Handle actions dispatched via the event system | | `VisibilityProvider` | Enable conditional rendering based on state | | `ValidationProvider` | Form field validation | ### External Store (Controlled Mode) Pass a `StateStore` to `StateProvider` (or `JSONUIProvider` / `createRenderer`) to use external state management (Redux, Zustand, XState, etc.): ```tsx import { createStateStore, type StateStore } from "@json-render/react"; const store = createStateStore({ count: 0 }); <StateProvider store={store}>{children}</StateProvider> // Mutate from anywhere — React re-renders automatically: store.set("/count", 1); ``` When `store` is provided, `initialState` and `onStateChange` are ignored. ## Dynamic Prop Expressions Any prop value can be a data-driven expression resolved by the renderer before components receive props: - **`{ "$state": "/state/key" }`** - reads from 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> }`** - conditional value - **`{ "$template": "Hello, ${/name}!" }`** - interpolates state values into strings - **`{ "$computed": "fn", "args": { ... } }`**