npx skills add ...
npx skills add vercel-labs/json-render --skill react-native
npx skills add vercel-labs/json-render --skill 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.
React Native renderer that converts JSON specs into native mobile component trees with standard components, data binding, visibility, actions, and dynamic props.
Container - wrapper with padding, background, border radiusRow - horizontal flex layout with gap, alignmentColumn - vertical flex layout with gap, alignmentScrollContainer - scrollable area (vertical or horizontal)SafeArea - safe area insets for notch/home indicatorPressable - touchable wrapper that triggers actions on pressSpacer - fixed or flexible spacingDivider - thin line separatorHeading - heading text (levels 1-6)Paragraph - body textLabel - small label textImage - image display with sizing modesAvatar - circular avatar imageBadge - small status badgeChip - tag/chip for categoriesButton - pressable button with variantsTextInput - text input fieldSwitch - toggle switchCheckbox - checkbox with labelSlider - range sliderSearchBar - search inputSpinner - loading indicatorProgressBar - progress indicatorCard - card container with optional headerListItem - list row with title, subtitle, accessoryModal - bottom sheet modalUse visible on elements. Syntax: { "$state": "/path" }, { "$state": "/path", "eq": value }, { "$state": "/path", "not": true }, [ cond1, cond2 ] for AND.
Use Pressable with the built-in setState action for interactive UIs like tab bars:
Any prop value can be a data-driven expression resolved at render time:
{ "$state": "/state/key" } - reads from state model (one-way read){ "$bindState": "/path" } - two-way binding: 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 valueComponents do not use a statePath prop for two-way binding. Use { "$bindState": "/path" } on the natural value prop instead.
The setState action is handled automatically by ActionProvider and updates the state model directly, which re-evaluates visibility conditions and dynamic prop expressions:
| Provider | Purpose |
|---|---|
StateProvider | Share state across components (JSON Pointer paths). Accepts optional store prop for controlled mode. |
ActionProvider | Handle actions dispatched from components |
VisibilityProvider | Enable conditional rendering based on state |
ValidationProvider | Form field validation |
Pass a StateStore to StateProvider (or JSONUIProvider / createRenderer) to use external state management:
When store is provided, initialState and onStateChange are ignored.
| Export | Purpose |
|---|---|
defineRegistry | Create a type-safe component registry from a catalog |
Renderer | Render a spec using a registry |
schema | React Native element tree schema |
standardComponentDefinitions | Catalog definitions for all standard components |
standardActionDefinitions | Catalog definitions for standard actions |
standardComponents | Pre-built component implementations |
createStandardActionHandlers | Create handlers for standard actions |
useStateStore | Access state context |
useStateValue | Get single value from state |
useBoundProp | Two-way state binding via $bindState/$bindItem |
useStateBinding | (deprecated) Legacy two-way binding by path |
useActions | Access actions context |
useAction | Get a single action dispatch function |
useUIStream | Stream specs from an API endpoint |
createStateStore | Create a framework-agnostic in-memory StateStore |
StateStore | Interface for plugging in external state management |
{
"type": "Pressable",
"props": {
"action": "setState",
"actionParams": { "statePath": "/activeTab", "value": "home" }
},
"children": ["home-icon", "home-label"]
}{
"type": "TextInput",
"props": {
"value": { "$bindState": "/form/email" },
"placeholder": "Email"
}
}{ "action": "setState", "actionParams": { "statePath": "/activeTab", "value": "home" } }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