Json-render react-native
React Native renderer for json-render that turns JSON specs into native mobile UIs. Use when working with @json-render/react-native, building React Native UIs from JSON, creating mobile component catalogs, or rendering AI-generated specs on mobile.
install
source · Clone the upstream repo
git clone https://github.com/vercel-labs/json-render
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/vercel-labs/json-render "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/react-native" ~/.claude/skills/vercel-labs-json-render-react-native && rm -rf "$T"
manifest:
skills/react-native/SKILL.mdsource content
@json-render/react-native
React Native renderer that converts JSON specs into native mobile component trees with standard components, data binding, visibility, actions, and dynamic props.
Quick Start
import { defineCatalog } from "@json-render/core"; import { schema } from "@json-render/react-native/schema"; import { standardComponentDefinitions, standardActionDefinitions, } from "@json-render/react-native/catalog"; import { defineRegistry, Renderer, type Components } from "@json-render/react-native"; import { z } from "zod"; // Create catalog with standard + custom components const catalog = defineCatalog(schema, { components: { ...standardComponentDefinitions, Icon: { props: z.object({ name: z.string(), size: z.number().nullable(), color: z.string().nullable() }), slots: [], description: "Icon display", }, }, actions: standardActionDefinitions, }); // Register only custom components (standard ones are built-in) const { registry } = defineRegistry(catalog, { components: { Icon: ({ props }) => <Ionicons name={props.name} size={props.size ?? 24} />, } as Components<typeof catalog>, }); // Render function App({ spec }) { return ( <StateProvider initialState={{}}> <VisibilityProvider> <ActionProvider handlers={{}}> <Renderer spec={spec} registry={registry} /> </ActionProvider> </VisibilityProvider> </StateProvider> ); }
Standard Components
Layout
- wrapper with padding, background, border radiusContainer
- horizontal flex layout with gap, alignmentRow
- vertical flex layout with gap, alignmentColumn
- scrollable area (vertical or horizontal)ScrollContainer
- safe area insets for notch/home indicatorSafeArea
- touchable wrapper that triggers actions on pressPressable
- fixed or flexible spacingSpacer
- thin line separatorDivider
Content
- heading text (levels 1-6)Heading
- body textParagraph
- small label textLabel
- image display with sizing modesImage
- circular avatar imageAvatar
- small status badgeBadge
- tag/chip for categoriesChip
Input
- pressable button with variantsButton
- text input fieldTextInput
- toggle switchSwitch
- checkbox with labelCheckbox
- range sliderSlider
- search inputSearchBar
Feedback
- loading indicatorSpinner
- progress indicatorProgressBar
Composite
- card container with optional headerCard
- list row with title, subtitle, accessoryListItem
- bottom sheet modalModal
Visibility Conditions
Use
visible on elements. Syntax: { "$state": "/path" }, { "$state": "/path", "eq": value }, { "$state": "/path", "not": true }, [ cond1, cond2 ] for AND.
Pressable + setState Pattern
Use
Pressable with the built-in setState action for interactive UIs like tab bars:
{ "type": "Pressable", "props": { "action": "setState", "actionParams": { "statePath": "/activeTab", "value": "home" } }, "children": ["home-icon", "home-label"] }
Dynamic Prop Expressions
Any prop value can be a data-driven expression resolved at render time:
- reads from state model (one-way read){ "$state": "/state/key" }
- two-way binding: use on the natural value prop (value, checked, pressed, etc.) of form components.{ "$bindState": "/path" }
- two-way binding to a repeat item field. Use inside repeat scopes.{ "$bindItem": "field" }
- conditional value{ "$cond": <condition>, "$then": <value>, "$else": <value> }
{ "type": "TextInput", "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.
Built-in Actions
The
setState action is handled automatically by ActionProvider and updates the state model directly, which re-evaluates visibility conditions and dynamic prop expressions:
{ "action": "setState", "actionParams": { "statePath": "/activeTab", "value": "home" } }
Providers
| Provider | Purpose |
|---|---|
| Share state across components (JSON Pointer paths). Accepts optional prop for controlled mode. |
| Handle actions dispatched from components |
| Enable conditional rendering based on state |
| Form field validation |
External Store (Controlled Mode)
Pass a
StateStore to StateProvider (or JSONUIProvider / createRenderer) to use external state management:
import { createStateStore, type StateStore } from "@json-render/react-native"; const store = createStateStore({ count: 0 }); <StateProvider store={store}>{children}</StateProvider> store.set("/count", 1); // React re-renders automatically
When
store is provided, initialState and onStateChange are ignored.
Key Exports
| Export | Purpose |
|---|---|
| Create a type-safe component registry from a catalog |
| Render a spec using a registry |
| React Native element tree schema |
| Catalog definitions for all standard components |
| Catalog definitions for standard actions |
| Pre-built component implementations |
| Create handlers for standard actions |
| Access state context |
| Get single value from state |
| Two-way state binding via / |
| (deprecated) Legacy two-way binding by path |
| Access actions context |
| Get a single action dispatch function |
| Stream specs from an API endpoint |
| Create a framework-agnostic in-memory |
| Interface for plugging in external state management |