Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
vercel-labs avatar

React

  • 4.9k installs
  • 15.8k repo stars
  • Updated July 8, 2026
  • vercel-labs/json-render

A React renderer library that converts JSON element tree specifications into rendered React component trees using a type-safe catalog system with state management.

About

React renderer that transforms JSON specs into React component trees using a catalog-based architecture. Developers use it when building dynamic UIs from JSON, creating AI-generated component specs, or developing component catalogs without writing JSX directly. Key workflows include defining typed component catalogs with Zod schemas, implementing state management via StateProvider with optional external stores (Redux, Zustand), enabling two-way prop binding via $bindState expressions, and handling events through an action system. Built-in actions (setState, pushState, removeState, validateForm) manage state mutations automatically. Supports visibility conditions, computed expressions, state watchers, and validation providers for form fields. Define type-safe component catalogs with Zod schema validation for props Two-way binding via $bindState on component value props with useBoundProp hook Built-in state actions (setState, pushState, removeState, validateForm) require no catalog declaration External state store integration for Redux, Zustand, XState via StateStore interface Event system with action dispatching, visibility conditions, and state watchers on elements Convert JSON el.

  • Define type-safe component catalogs with Zod schema validation for props
  • Two-way binding via $bindState on component value props with useBoundProp hook
  • Built-in state actions (setState, pushState, removeState, validateForm) require no catalog declaration
  • External state store integration for Redux, Zustand, XState via StateStore interface
  • Event system with action dispatching, visibility conditions, and state watchers on elements

React by the numbers

  • 4,892 all-time installs (skills.sh)
  • +429 installs in the week ending Jul 28, 2026 (Skillselion tracking)
  • Ranked #100 of 2,277 Frontend Development skills by installs in the Skillselion catalog
  • Security screen: LOW risk (skills.sh audit)
  • Data as of Jul 28, 2026 (Skillselion catalog sync)
At a glance

react capabilities & compatibility

free

Capabilities
convert json element trees to react components · type safe prop validation via zod schemas · two way state binding ($bindstate, $binditem exp · action dispatching and event handling · conditional rendering with visibility rules · external state store integration · form validation via validationprovider · computed expressions and template interpolation
Use cases
frontend · ui design · api development
Platforms
macOS · Windows · Linux · WSL
Runs
Runs locally
Pricing
Free
From the docs

What react says it does

React renderer that converts JSON specs into React component trees.
README (header)
npx skills add https://github.com/vercel-labs/json-render --skill react

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs4.9k
repo stars15.8k
Security audit3 / 3 scanners passed
Last updatedJuly 8, 2026
Repositoryvercel-labs/json-render

What it does

Convert JSON specifications into type-safe React component trees with state management, two-way binding, and event handling.

Who is it for?

AI-generated UI specs, dynamic component catalogs, form-heavy apps with JSON-driven layouts, design systems that need JSON serialization.

Skip if: Static pages, apps that require custom JSX, low-latency real-time UIs without state abstraction.

When should I use this skill?

You need to render React UIs from JSON specs, integrate external state stores, or build JSON-serializable component trees.

What you get

Type-safe React components rendered from JSON with full state control, event handling, validation, and external state management integration.

  • Type-safe component registry
  • Rendered React component tree
  • State management context

By the numbers

  • 5 built-in actions included (setState, pushState, removeState, validateForm, plus custom)
  • 10+ key exports (defineRegistry, Renderer, useStateStore, useBoundProp, createStateStore, etc.)
  • 4 provider types (StateProvider, ActionProvider, VisibilityProvider, ValidationProvider)

Files

SKILL.mdMarkdownGitHub ↗

@json-render/react

React renderer that converts JSON specs into React component trees.

Quick Start

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

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:

{
  "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

ProviderPurpose
StateProviderShare state across components (JSON Pointer paths). Accepts optional store prop for controlled mode.
ActionProviderHandle actions dispatched via the event system
VisibilityProviderEnable conditional rendering based on state
ValidationProviderForm field validation

External Store (Controlled Mode)

Pass a StateStore to StateProvider (or JSONUIProvider / createRenderer) to use external state management (Redux, Zustand, XState, etc.):

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.
  • Filtered lists: repeat plus an $item visible condition on the same container renders only matching items: { "repeat": { "statePath": "/tasks", "key": "id" }, "visible": { "$item": "status", "eq": "todo" }, "children": ["task-card"] }. AND-composed $state conjuncts gate the container shell; $item/$index conjuncts filter items.
  • `{ "$cond": <condition>, "$then": <value>, "$else": <value> }` - conditional value
  • `{ "$template": "Hello, ${/name}!" }` - interpolates state values into strings
  • `{ "$computed": "fn", "args": { ... } }` - calls registered functions with resolved args
{
  "type": "Input",
  "props": {
    "value": { "$bindState": "/form/email" },
    "placeholder": "Email"
  }
}

Components do not use a statePath prop for two-way binding. Use { "$bindState": "/path" } on the natural value prop instead.

Components receive already-resolved props. For two-way bound props, use the useBoundProp hook with the bindings map the renderer provides.

Register $computed functions via the functions prop on JSONUIProvider or createRenderer:

<JSONUIProvider
  functions={{ fullName: (args) => `${args.first} ${args.last}` }}
>

Event System

Components use emit to fire named events, or on() to get an event handle with metadata. The element's on field maps events to action bindings:

// Simple event firing
Button: ({ props, emit }) => (
  <button onClick={() => emit("press")}>{props.label}</button>
),

// Event handle with metadata (e.g. preventDefault)
Link: ({ props, on }) => {
  const click = on("click");
  return (
    <a href={props.href} onClick={(e) => {
      if (click.shouldPreventDefault) e.preventDefault();
      click.emit();
    }}>{props.label}</a>
  );
},
{
  "type": "Button",
  "props": { "label": "Submit" },
  "on": { "press": { "action": "submit" } }
}

The EventHandle returned by on() has: emit(), shouldPreventDefault (boolean), and bound (boolean).

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" } },
  "children": []
}

Built-in Actions

The setState, pushState, removeState, and validateForm actions are built into the React schema and handled automatically by ActionProvider. They are injected into AI prompts without needing to be declared in catalog actions:

{ "action": "setState", "params": { "statePath": "/activeTab", "value": "home" } }
{ "action": "pushState", "params": { "statePath": "/items", "value": { "text": "New" } } }
{ "action": "removeState", "params": { "statePath": "/items", "index": 0 } }
{ "action": "validateForm", "params": { "statePath": "/formResult" } }

validateForm validates all registered fields and writes { valid, errors } to state.

Note: statePath in action params (e.g. setState.statePath) targets the mutation path. Two-way binding in component props uses { "$bindState": "/path" } on the value prop, not statePath.

useBoundProp

For form components that need two-way binding, use useBoundProp with the bindings map the renderer provides when a prop uses { "$bindState": "/path" } or { "$bindItem": "field" }:

import { useBoundProp } from "@json-render/react";

Input: ({ element, bindings }) => {
  const [value, setValue] = useBoundProp<string>(
    element.props.value,
    bindings?.value
  );
  return (
    <input
      value={value ?? ""}
      onChange={(e) => setValue(e.target.value)}
    />
  );
},

useBoundProp(propValue, bindingPath) returns [value, setValue]. The value is the resolved prop; setValue writes back to the bound state path (no-op if not bound).

BaseComponentProps

For building reusable component libraries not tied to a specific catalog (e.g. @json-render/shadcn):

import type { BaseComponentProps } from "@json-render/react";

const Card = ({ props, children }: BaseComponentProps<{ title?: string }>) => (
  <div>{props.title}{children}</div>
);

defineRegistry

defineRegistry conditionally requires the actions field only when the catalog declares actions. Catalogs with actions: {} can omit it.

Key Exports

ExportPurpose
defineRegistryCreate a type-safe component registry from a catalog
RendererRender a spec using a registry
schemaElement tree schema (includes built-in state actions: setState, pushState, removeState, validateForm)
useStateStoreAccess state context
useStateValueGet single value from state
useBoundPropTwo-way binding for $bindState/$bindItem expressions
useActionsAccess actions context
useActionGet a single action dispatch function
useOptionalValidationNon-throwing variant of useValidation (returns null if no provider)
useUIStreamStream specs from an API endpoint
createStateStoreCreate a framework-agnostic in-memory StateStore
StateStoreInterface for plugging in external state management
BaseComponentPropsCatalog-agnostic base type for reusable component libraries
EventHandleEvent handle type (emit, shouldPreventDefault, bound)
ComponentContextTyped component context (catalog-aware)

Related skills

How it compares

Similar to form developers or low-code platforms, but JSON-first and headless; integrates into React apps rather than providing hosted UI editor.

FAQ

Do I need to define actions in the catalog if I use built-in actions?

No. setState, pushState, removeState, and validateForm are built into the React schema and handled automatically by ActionProvider without catalog declaration.

How do I enable two-way binding on form fields?

Use { "$bindState": "/path" } on the component's natural value prop (value, checked, etc.), then use useBoundProp hook in the component implementation to read and write back.

Can I use an external state store like Redux?

Yes. Pass a StateStore to StateProvider (or createRenderer). The store interface is framework-agnostic; set values via store.set("/path", value) and React re-renders automatically.

Is 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.

Frontend Developmentfrontendintegrations

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.