diff --git a/apps/web/app/(main)/docs/api/react-native/page.mdx b/apps/web/app/(main)/docs/api/react-native/page.mdx
index 1385b050..6f267802 100644
--- a/apps/web/app/(main)/docs/api/react-native/page.mdx
+++ b/apps/web/app/(main)/docs/api/react-native/page.mdx
@@ -149,6 +149,40 @@ Conditions in specs use the `VisibilityCondition` format with `$state` paths (e.
```
+### JSONUIProvider
+
+Convenience wrapper that combines `StateProvider`, `VisibilityProvider`, `ActionProvider`, and `ValidationProvider`. It accepts the same props as those providers, plus:
+
+
+
+
+ | Prop |
+ Type |
+ Description |
+
+
+
+
+ functions |
+ Record<string, ComputedFunction> |
+ Named functions for $computed expressions in props |
+
+
+
+
+```tsx
+ `${args.first} ${args.last}` }}
+>
+
+
+```
+
+The `functions` prop is also available on `createRenderer`.
+
## defineRegistry
Create a type-safe component registry. Standard components are built-in; only register custom components.
diff --git a/packages/react-native/README.md b/packages/react-native/README.md
index 3c09c67f..69d39321 100644
--- a/packages/react-native/README.md
+++ b/packages/react-native/README.md
@@ -195,6 +195,33 @@ Any prop value can be a dynamic expression resolved at render time:
See [@json-render/core](../core/README.md) for full expression syntax.
+### `$template` and `$computed`
+
+```json
+{
+ "label": { "$template": "Hello, ${/user/name}!" },
+ "fullName": {
+ "$computed": "fullName",
+ "args": {
+ "first": { "$state": "/form/firstName" },
+ "last": { "$state": "/form/lastName" }
+ }
+ }
+}
+```
+
+Register named functions via the `functions` prop on `JSONUIProvider` or `createRenderer` (same as [@json-render/react](../react/README.md)):
+
+```tsx
+ `${args.first} ${args.last}` }}
+>
+
+
+```
+
## Tab Navigation Pattern
Combine `Pressable`, `setState`, visibility conditions, and dynamic props for functional tabs:
diff --git a/packages/react-native/src/renderer.tsx b/packages/react-native/src/renderer.tsx
index f9601a8b..d7c96e69 100644
--- a/packages/react-native/src/renderer.tsx
+++ b/packages/react-native/src/renderer.tsx
@@ -12,6 +12,7 @@ import type {
Catalog,
SchemaDefinition,
StateStore,
+ ComputedFunction,
} from "@json-render/core";
import {
resolveElementProps,
@@ -136,6 +137,19 @@ class ElementErrorBoundary extends React.Component<
}
}
+// ---------------------------------------------------------------------------
+// FunctionsContext – provides $computed functions to the element tree
+// ---------------------------------------------------------------------------
+
+const EMPTY_FUNCTIONS: Record = {};
+
+const FunctionsContext =
+ React.createContext>(EMPTY_FUNCTIONS);
+
+function useFunctions(): Record {
+ return React.useContext(FunctionsContext);
+}
+
interface ElementRendererProps {
element: UIElement;
spec: Spec;
@@ -159,20 +173,21 @@ const ElementRenderer = React.memo(function ElementRenderer({
const { ctx } = useVisibility();
const { execute } = useActions();
const { getSnapshot } = useStateStore();
-
- // Build context with repeat scope (used for both visibility and props)
- const fullCtx: PropResolutionContext = useMemo(
- () =>
- repeatScope
- ? {
- ...ctx,
- repeatItem: repeatScope.item,
- repeatIndex: repeatScope.index,
- repeatBasePath: repeatScope.basePath,
- }
- : ctx,
- [ctx, repeatScope],
- );
+ const functions = useFunctions();
+
+ // Build context with repeat scope and $computed functions (visibility + props)
+ const fullCtx: PropResolutionContext = useMemo(() => {
+ const base: PropResolutionContext = repeatScope
+ ? {
+ ...ctx,
+ repeatItem: repeatScope.item,
+ repeatIndex: repeatScope.index,
+ repeatBasePath: repeatScope.basePath,
+ }
+ : { ...ctx };
+ base.functions = functions;
+ return base;
+ }, [ctx, repeatScope, functions]);
// Evaluate visibility (now supports $item/$index inside repeat scopes)
const isVisible =
@@ -439,6 +454,8 @@ export interface JSONUIProviderProps {
string,
(value: unknown, args?: Record) => boolean
>;
+ /** Named functions for `$computed` expressions in props */
+ functions?: Record;
/** Callback when state changes (uncontrolled mode) */
onStateChange?: (changes: Array<{ path: string; value: unknown }>) => void;
children: ReactNode;
@@ -454,6 +471,7 @@ export function JSONUIProvider({
handlers,
navigate,
validationFunctions,
+ functions,
onStateChange,
children,
}: JSONUIProviderProps) {
@@ -465,10 +483,12 @@ export function JSONUIProvider({
>
-
- {children}
-
-
+
+
+ {children}
+
+
+
@@ -663,6 +683,8 @@ export interface CreateRendererProps {
onAction?: (actionName: string, params?: Record) => void;
/** Callback when state changes (uncontrolled mode) */
onStateChange?: (changes: Array<{ path: string; value: unknown }>) => void;
+ /** Named functions for `$computed` expressions in props */
+ functions?: Record;
/** Whether the spec is currently loading/streaming */
loading?: boolean;
/** Fallback component for unknown types */
@@ -716,6 +738,7 @@ export function createRenderer<
state,
onAction,
onStateChange,
+ functions,
loading,
fallback,
}: CreateRendererProps) {
@@ -744,15 +767,17 @@ export function createRenderer<
>
-
-
-
-
+
+
+
+
+
+
diff --git a/skills/react-native/SKILL.md b/skills/react-native/SKILL.md
index f43134b8..ff521ac0 100644
--- a/skills/react-native/SKILL.md
+++ b/skills/react-native/SKILL.md
@@ -118,6 +118,7 @@ Any prop value can be a data-driven expression resolved at render time:
- **`{ "$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": , "$then": , "$else": }`** - conditional value
+- **`{ "$computed": "name", "args": { ... } }`** - call a named function; register implementations with the `functions` prop on `JSONUIProvider` or `createRenderer` (same as `@json-render/react`)
```json
{
@@ -147,6 +148,7 @@ The `setState` action is handled automatically by `ActionProvider` and updates t
| `ActionProvider` | Handle actions dispatched from components |
| `VisibilityProvider` | Enable conditional rendering based on state |
| `ValidationProvider` | Form field validation |
+| `JSONUIProvider` | Combines the above providers; also accepts `functions` for `$computed` props |
### External Store (Controlled Mode)