|
|
5 mēneši atpakaļ | |
|---|---|---|
| .. | ||
| src | 5 mēneši atpakaļ | |
| CHANGELOG.md | 5 mēneši atpakaļ | |
| README.md | 5 mēneši atpakaļ | |
| package.json | 5 mēneši atpakaļ | |
| tsconfig.json | 5 mēneši atpakaļ | |
| tsup.config.ts | 5 mēneši atpakaļ | |
React Native renderer for json-render. Turn JSON specs into native mobile UIs with standard components, data binding, visibility, actions, and dynamic props.
npm install @json-render/react-native @json-render/core zod
import { defineCatalog } from "@json-render/core";
import { schema } from "@json-render/react-native/schema";
import {
standardComponentDefinitions,
standardActionDefinitions,
} from "@json-render/react-native/catalog";
import { z } from "zod";
export const catalog = defineCatalog(schema, {
components: {
...standardComponentDefinitions,
// Add custom components
Icon: {
props: z.object({
name: z.string(),
size: z.number().nullable(),
color: z.string().nullable(),
}),
slots: [],
description: "Icon display using Ionicons",
},
},
actions: standardActionDefinitions,
});
import { defineRegistry, type Components } from "@json-render/react-native";
import Ionicons from "@expo/vector-icons/Ionicons";
import { catalog } from "./catalog";
export const { registry } = defineRegistry(catalog, {
components: {
Icon: ({ props }) => (
<Ionicons
name={props.name as keyof typeof Ionicons.glyphMap}
size={props.size ?? 24}
color={props.color ?? "#111827"}
/>
),
} as Components<typeof catalog>,
});
Standard components (Container, Row, Column, Button, TextInput, etc.) are included by default. You only need to register custom ones.
import {
Renderer,
StateProvider,
VisibilityProvider,
ActionProvider,
ValidationProvider,
} from "@json-render/react-native";
import { registry } from "./registry";
function App({ spec }) {
return (
<StateProvider initialState={{}}>
<VisibilityProvider>
<ActionProvider handlers={{}}>
<ValidationProvider>
<Renderer spec={spec} registry={registry} />
</ValidationProvider>
</ActionProvider>
</VisibilityProvider>
</StateProvider>
);
}
| Component | Description |
|---|---|
Container |
Basic wrapper with padding, background, border radius |
Row |
Horizontal flex layout with gap, alignment, flex |
Column |
Vertical flex layout with gap, alignment, flex |
ScrollContainer |
Scrollable area (vertical or horizontal) |
SafeArea |
Safe area insets for notch/home indicator |
Pressable |
Touchable wrapper that triggers actions on press |
Spacer |
Fixed or flexible spacing between elements |
Divider |
Thin line separator |
| Component | Description |
|---|---|
Heading |
Heading text (levels 1-6) |
Paragraph |
Body text |
Label |
Small label text |
Image |
Image display with sizing modes |
Avatar |
Circular avatar image |
Badge |
Small status badge |
Chip |
Tag/chip for categories |
| Component | Description |
|---|---|
Button |
Pressable button with variants |
TextInput |
Text input field |
Switch |
Toggle switch |
Checkbox |
Checkbox with label |
Slider |
Range slider |
SearchBar |
Search input |
| Component | Description |
|---|---|
Spinner |
Loading indicator |
ProgressBar |
Progress indicator |
| Component | Description |
|---|---|
Card |
Card container with optional header |
ListItem |
List row with title, subtitle, accessory |
Modal |
Bottom sheet modal |
The Pressable component wraps children and triggers an action on press. It's essential for building interactive UIs like tab bars:
{
"type": "Pressable",
"props": {
"action": "setState",
"actionParams": { "path": "/activeTab", "value": "home" }
},
"children": ["home-tab-icon", "home-tab-label"]
}
The setState action is handled automatically by ActionProvider. It updates the state model, which triggers re-evaluation of visibility conditions and dynamic prop expressions:
{
"action": "setState",
"actionParams": { "path": "/activeTab", "value": "home" }
}
Any prop value can be a dynamic expression resolved at render time:
{
"type": "Icon",
"props": {
"name": {
"$cond": { "eq": [{ "path": "/activeTab" }, "home"] },
"$then": "home",
"$else": "home-outline"
},
"color": {
"$cond": { "eq": [{ "path": "/activeTab" }, "home"] },
"$then": "#007AFF",
"$else": "#8E8E93"
}
}
}
See @json-render/core for full expression syntax.
Combine Pressable, setState, visibility conditions, and dynamic props for functional tabs:
Pressable with action: "setState" and actionParams: { path: "/activeTab", value: "tabName" }$cond dynamic props for active/inactive stylingvisible conditions: { "eq": [{ "path": "/activeTab" }, "tabName"] }const systemPrompt = catalog.prompt({
customRules: [
"Use SafeArea as the root element",
"Use Pressable + setState for interactive tabs",
],
});
| Hook | Purpose |
|---|---|
useStateStore() |
Access data context (data, get, set) |
useStateValue(path) |
Get single value from data |
useStateBinding(path) |
Two-way data binding (returns [value, setValue]) |
useVisibility() |
Access visibility evaluation |
useIsVisible(condition) |
Check if condition is met |
useActions() |
Access action context |
useAction(name) |
Get a single action dispatch function |
useUIStream(options) |
Stream specs from an API endpoint |
createStandardActionHandlers(options) |
Create handlers for standard actions |