Skip to main content
skill Claude Code CX Codex Cursor GitHub Copilot

react — Claude Code skill: what it does, how to install

This skill turns json-render JSON specs into React component trees. Use it when building JSON-driven UIs or defining a component catalog for generated specs.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add vercel-labs/json-render --skill react -a claude-code
Open on GitHub

What react does

The skill covers @json-render/react, where a JSON specification becomes a React component tree through Renderer. Components are wired through a registry created with defineRegistry. A catalog defines component props, zod schemas, slots, and descriptions before implementations are registered.

The documented patterns include default and named slots, visibility conditions, state, actions, validation, and an external store. Prop expressions support state reads, two-way bindings, repeated items, templates, and computed values. Components can emit named events, while the element’s on field connects those events to actions.

The catalog example uses @json-render/core, @json-render/react, and zod. This skill targets React projects, so a non-React application is a compatibility check before installation.

What it helps with

  • turn a JSON specification into a React component tree
  • define typed props for a reusable component catalog
  • pass content through named slots
  • connect rendered UI to state and actions

Example prompts for the agent

  • Create a catalog with `defineCatalog` and `defineRegistry`, then render the JSON specification with `Renderer`.
  • Define a `Card` with a title and `default` slot, then connect a `Layout` to its `header` and `footer` slots.
  • Bind a form field with `{ "$bindState": "/form/email" }` and use `visible` to render an element only when its condition matches.

How to install react

1Skills CLI (Vercel)

npx skills add vercel-labs/json-render --skill react -a claude-code

2Manually: put the files in place

~/.claude/skills/react/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/react/

From the same set5

The whole set →

Similar

nextjs-app-router-patterns Seth Hobson This skill covers Next.js App Router patterns for rendering, routing, and data fetching. Use it when starting an App Router project or moving from Pages Router. Claude Code CX Codex Cursor 32,529installs fixing-metadata ibelick Audits page metadata and fixes what's broken: titles, descriptions, canonical URLs, Open Graph, Twitter cards, icons and JSON-LD. Claude Code CX Codex Cursor +1 18,200installs core-web-vitals web-quality-skills A skill that measures first, then fixes LCP, INP and CLS. Handy when a page loads slowly or the layout jumps around. Claude Code CX Codex Cursor +1 31,900installs vue-best-practices github.com/vuejs-ai Enforces Vue 3 guidelines using Composition API and TypeScript. Helpful for structuring SFCs, managing reactivity, and writing composables. Claude Code CX Codex Cursor +1 42,700installs chakra-ui-builder chakra-ui Build responsive, accessible interfaces with Chakra UI v3, add the library to a project, and shape its theme. Useful for components, pages, and layouts. Claude Code CX Codex Cursor +1 3,500installs modern-web-guidance GoogleChrome A skill that searches a catalog of modern web guides and feeds the matching one to your agent. Useful when the model's training data has stale HTML, CSS or client-side JS patterns. Claude Code CX Codex Cursor +1 34,900installs frontend-design official anthropics This skill guides intentional visual design for new interfaces and redesigns. Use it when a generic generated UI is not enough. Claude Code Claude.ai 1,025,790installs design-taste-frontend Leonxlnx This skill directs the design of public pages and work screens, then checks the result in a browser through screenshots. Use it when an interface feels generic or unfinished. Claude Code CX Codex Cursor +1 673,100installs

Questions about react

How do I install react in Claude Code?

Run npx skills add vercel-labs/json-render --skill react -a claude-code, then run npx skills add vercel-labs/json-render --skill react -a claude-code again. The skill file is placed at ~/.claude/skills/react/SKILL.md.

What does react do?

The skill explains how to use @json-render/react to turn JSON specifications into React components. It also covers component catalogs, slots, state, visibility, computed props, and events.

What do I need to create a component catalog?

Create the catalog with defineCatalog and the @json-render/react/schema schema. The example uses zod for prop definitions, then registers implementations with defineRegistry.

Source text

SKILL.md · Apache-2.0
---
name: react
description: 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.
---

# @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() }),
      slots: ["default"],
      description: "Card container with title",
    },
    Layout: {
      props: z.object({}),
      slots: ["default", "header", "footer"],
      description: "Layout with named content regions",
    },
  },
});

// 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>
    ),
    Layout: ({ children, slots }) => (
      <div>
        <header>{slots?.header}</header>
        <main>{children}</main>
        <footer>{slots?.footer}</footer>
      </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" } }]
  }
}
```

## Named Slots

Use `children` for the `"default"` slot. Use the element's top-level `slots` object for other slot names declared by the catalog:

```json
{
  "type": "Layout",
  "props": {},
  "children": ["main"],
  "slots": {
    "header": ["heading"],
    "footer": ["actions"]
  }
}
```

Registry components receive named content as `slots?.header`, `slots?.footer`, and so on. Do not use `slots.default`.

## 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.
- **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.
- **Nested lists**: inside a repeat, use `{ "repeat": { "statePath": { "$item": "comments" }, "key": "id" } }` to iterate an array on the enclosing item.
- **`{ "$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

```json
{
  "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`:

```tsx
<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:

```tsx
// 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>
  );
},
```

```json
{
  "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:

```json
{
  "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`:

```json
{ "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" }`:

```tsx
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`):

```typescript
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

| Export                  | Purpose                                                                                               |
| ----------------------- | ----------------------------------------------------------------------------------------------------- |
| `defineRegistry`        | Create a type-safe component registry from a catalog                                                  |
| `Renderer`              | Render a spec using a registry                                                                        |
| `schema`                | Element tree schema (includes built-in state actions: setState, pushState, removeState, validateForm) |
| `useStateStore`         | Access state context                                                                                  |
| `useStateValue`         | Get single value from state                                                                           |
| `useBoundProp`          | Two-way binding for `$bindState`/`$bindItem` expressions                                              |
| `useActions`            | Access actions context                                                                                |
| `useAction`             | Get a single action dispatch function                                                                 |
| `useOptionalValidation` | Non-throwing variant of useValidation (returns null if no provider)                                   |
| `useUIStream`           | Stream specs from an API endpoint                                                                     |
| `createStateStore`      | Create a framework-agnostic in-memory `StateStore`                                                    |
| `StateStore`            | Interface for plugging in external state management                                                   |
| `BaseComponentProps`    | Catalog-agnostic base type for reusable component libraries                                           |
| `EventHandle`           | Event handle type (`emit`, `shouldPreventDefault`, `bound`)                                           |
| `ComponentContext`      | Typed component context (catalog-aware)                                                               |

© vercel-labs · Apache-2.0 · vercel-labs/json-render

See also

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor