Skip to content

Commit efff9b9

Browse files
committed
prepare v0.10
1 parent 3c11f19 commit efff9b9

File tree

20 files changed

+578
-63
lines changed

20 files changed

+578
-63
lines changed

.changeset/config.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
"@json-render/codegen",
1414
"@json-render/zustand",
1515
"@json-render/redux",
16-
"@json-render/jotai"
16+
"@json-render/jotai",
17+
"@json-render/vue",
18+
"@json-render/xstate"
1719
]
1820
],
1921
"linked": [],

.changeset/v0-10-release.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
---
2+
"@json-render/core": minor
3+
"@json-render/react": minor
4+
"@json-render/shadcn": minor
5+
"@json-render/vue": minor
6+
"@json-render/xstate": minor
7+
---
8+
9+
Dynamic forms, Vue renderer, XState Store adapter, and computed values.
10+
11+
### New: `@json-render/vue` Package
12+
13+
Vue 3 renderer for json-render. Full feature parity with `@json-render/react` including data binding, visibility conditions, actions, validation, repeat scopes, and streaming.
14+
- `defineRegistry` — create type-safe component registries from catalogs
15+
- `Renderer` — render specs as Vue component trees
16+
- Providers: `StateProvider`, `ActionProvider`, `VisibilityProvider`, `ValidationProvider`
17+
- Composables: `useStateStore`, `useStateValue`, `useStateBinding`, `useActions`, `useAction`, `useIsVisible`, `useFieldValidation`
18+
- Streaming: `useUIStream`, `useChatUI`
19+
- External store support via `StateStore` interface
20+
21+
### New: `@json-render/xstate` Package
22+
23+
XState Store (atom) adapter for json-render's `StateStore` interface. Wire an `@xstate/store` atom as the state backend.
24+
- `xstateStoreStateStore({ atom })` — creates a `StateStore` from an `@xstate/store` atom
25+
- Requires `@xstate/store` v3+
26+
27+
### New: `$computed` Expressions
28+
29+
Call registered functions from prop expressions:
30+
- `{ "$computed": "functionName", "args": { "key": <expression> } }` — calls a named function with resolved args
31+
- Functions registered via catalog and provided at runtime through `functions` prop on `JSONUIProvider` / `createRenderer`
32+
- `ComputedFunction` type exported from `@json-render/core`
33+
34+
### New: `$template` Expressions
35+
36+
Interpolate state values into strings:
37+
- `{ "$template": "Hello, ${/user/name}!" }` — replaces `${/path}` references with state values
38+
- Missing paths resolve to empty string
39+
40+
### New: State Watchers
41+
42+
React to state changes by triggering actions:
43+
- `watch` field on elements maps state paths to action bindings
44+
- Fires when watched values change (not on initial render)
45+
- Supports cascading dependencies (e.g. country → city loading)
46+
- `watch` is a top-level field on elements (sibling of type/props/children), not inside props
47+
- Spec validator detects and auto-fixes `watch` placed inside props
48+
49+
### New: Cross-Field Validation Functions
50+
51+
New built-in validation functions for cross-field comparisons:
52+
- `equalTo` — alias for `matches` with clearer semantics
53+
- `lessThan` — value must be less than another field (numbers, strings, coerced)
54+
- `greaterThan` — value must be greater than another field
55+
- `requiredIf` — required only when a condition field is truthy
56+
- Validation args now resolve through `resolvePropValue` for consistent `$state` expression handling
57+
58+
### New: `validateForm` Action (React)
59+
60+
Built-in action that validates all registered form fields at once:
61+
- Runs `validateAll()` synchronously and writes `{ valid, errors }` to state
62+
- Default state path: `/formValidation` (configurable via `statePath` param)
63+
- Added to React schema's built-in actions list
64+
65+
### Improved: shadcn/ui Validation
66+
67+
All form components now support validation:
68+
- Checkbox, Radio, Switch — added `checks` and `validateOn` props
69+
- Input, Textarea, Select — added `validateOn` prop (controls timing: change/blur/submit)
70+
- Shared validation schemas reduce catalog definition duplication
71+
72+
### Improved: React Provider Tree
73+
74+
Reordered provider nesting so `ValidationProvider` wraps `ActionProvider`, enabling `validateForm` to access validation state. Added `useOptionalValidation` hook for non-throwing access.

README.md

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ function Dashboard({ spec }) {
121121
| `@json-render/redux` | Redux / Redux Toolkit adapter for `StateStore` |
122122
| `@json-render/zustand` | Zustand adapter for `StateStore` |
123123
| `@json-render/jotai` | Jotai adapter for `StateStore` |
124-
| `@json-render/xstate-store` | XState Store (atom) adapter for `StateStore` |
124+
| `@json-render/xstate` | XState Store (atom) adapter for `StateStore` |
125125

126126
## Renderers
127127

@@ -342,10 +342,12 @@ Any prop value can be data-driven using expressions:
342342
}
343343
```
344344

345-
Two expression forms:
345+
Expression forms:
346346

347347
- **`{ "$state": "/state/key" }`** - reads a value from the state model
348-
- **`{ "$cond": <condition>, "$then": <value>, "$else": <value> }`** - evaluates a condition (same syntax as visibility conditions) and picks a branch
348+
- **`{ "$cond": <condition>, "$then": <value>, "$else": <value> }`** - evaluates a condition and picks a branch
349+
- **`{ "$template": "Hello, ${/user/name}!" }`** - interpolates state values into strings
350+
- **`{ "$computed": "fn", "args": { ... } }`** - calls a registered function with resolved args
349351

350352
### Actions
351353

@@ -361,6 +363,22 @@ Components can trigger actions, including the built-in `setState` action:
361363

362364
The `setState` action updates the state model directly, which re-evaluates visibility conditions and dynamic prop expressions.
363365

366+
### State Watchers
367+
368+
React to state changes by triggering actions:
369+
370+
```json
371+
{
372+
"type": "Select",
373+
"props": { "value": { "$bindState": "/form/country" }, "options": ["US", "Canada", "UK"] },
374+
"watch": {
375+
"/form/country": { "action": "loadCities", "params": { "country": { "$state": "/form/country" } } }
376+
}
377+
}
378+
```
379+
380+
`watch` is a top-level field on elements (sibling of `type`/`props`/`children`). Watchers fire when the watched value changes, not on initial render.
381+
364382
---
365383

366384
## Demo
@@ -376,6 +394,8 @@ pnpm dev
376394
- http://dashboard-demo.json-render.localhost:1355 - Example Dashboard
377395
- http://remotion-demo.json-render.localhost:1355 - Remotion Video Example
378396
- Chat Example: run `pnpm dev` in `examples/chat`
397+
- Vue Example: run `pnpm dev` in `examples/vue`
398+
- Vite Renderers (React + Vue): run `pnpm dev` in `examples/vite-renderers`
379399
- React Native example: run `npx expo start` in `examples/react-native`
380400

381401
## How It Works

apps/web/app/(main)/docs/api/react/page.mdx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,27 @@ const { registry } = defineRegistry(catalog, {
109109
type Registry = Record<string, React.ComponentType<ComponentRenderProps>>;
110110
```
111111

112+
### JSONUIProvider
113+
114+
Convenience wrapper that combines `StateProvider`, `VisibilityProvider`, `ValidationProvider`, and `ActionProvider`. Accepts all their props plus:
115+
116+
| Prop | Type | Description |
117+
|------|------|-------------|
118+
| `functions` | `Record<string, ComputedFunction>` | Named functions for `$computed` expressions in props |
119+
120+
```tsx
121+
<JSONUIProvider
122+
spec={spec}
123+
catalog={catalog}
124+
handlers={{ submit: async () => { /* ... */ } }}
125+
functions={{ fullName: (args) => `${args.first} ${args.last}` }}
126+
>
127+
<Renderer spec={spec} registry={registry} />
128+
</JSONUIProvider>
129+
```
130+
131+
The `functions` prop is also available on `createRenderer`.
132+
112133
### Component Props (via defineRegistry)
113134

114135
```tsx
@@ -237,6 +258,15 @@ const {
237258

238259
`ValidationConfig` is `{ checks?: ValidationCheck[], validateOn?: 'change' | 'blur' | 'submit' }`.
239260

261+
### useOptionalValidation
262+
263+
Non-throwing variant of `useValidation()`. Returns `null` when no `ValidationProvider` is present, instead of throwing. Useful in components that may or may not be rendered inside a validation context.
264+
265+
```typescript
266+
const validation = useOptionalValidation();
267+
// ValidationContextValue | null
268+
```
269+
240270
### useBoundProp
241271

242272
Two-way binding helper for `$bindState` / `$bindItem` expressions. Returns `[value, setValue]` where `setValue` writes back to the bound state path.

apps/web/app/(main)/docs/changelog/page.mdx

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,119 @@ export const metadata = pageMetadata("docs/changelog")
55

66
Notable changes and updates to json-render.
77

8+
## v0.10.0
9+
10+
February 2026
11+
12+
### New: `@json-render/vue`
13+
14+
Vue 3 renderer for json-render with full feature parity with `@json-render/react`. Data binding, visibility conditions, actions, validation, repeat scopes, streaming, and external store support.
15+
16+
```bash
17+
npm install @json-render/core @json-render/vue
18+
```
19+
20+
```typescript
21+
import { h } from "vue";
22+
import { defineRegistry, Renderer } from "@json-render/vue";
23+
import { schema } from "@json-render/vue/schema";
24+
25+
const { registry } = defineRegistry(catalog, {
26+
components: {
27+
Card: ({ props, children }) =>
28+
h("div", { class: "card" }, [h("h3", null, props.title), children]),
29+
Button: ({ props, emit }) =>
30+
h("button", { onClick: () => emit("press") }, props.label),
31+
},
32+
});
33+
```
34+
35+
Providers: `StateProvider`, `ActionProvider`, `VisibilityProvider`, `ValidationProvider`. Composables: `useStateStore`, `useStateValue`, `useActions`, `useAction`, `useIsVisible`, `useFieldValidation`, `useBoundProp`, `useUIStream`, `useChatUI`.
36+
37+
See the [Vue API reference](/docs/api/vue) for details.
38+
39+
### New: `@json-render/xstate`
40+
41+
[XState Store](https://stately.ai/docs/xstate-store) (atom) adapter for json-render's `StateStore` interface. Wire an `@xstate/store` atom as the state backend for any renderer.
42+
43+
```bash
44+
npm install @json-render/xstate @xstate/store
45+
```
46+
47+
```typescript
48+
import { createAtom } from "@xstate/store";
49+
import { xstateStoreStateStore } from "@json-render/xstate";
50+
51+
const atom = createAtom({ count: 0 });
52+
const store = xstateStoreStateStore({ atom });
53+
```
54+
55+
Requires `@xstate/store` v3+.
56+
57+
### New: `$computed` and `$template` Expressions
58+
59+
Two new prop expression types for dynamic values:
60+
61+
- **`$template`** -- interpolate state values into strings: `{ "$template": "Hello, ${/user/name}!" }`
62+
- **`$computed`** -- call registered functions: `{ "$computed": "fullName", "args": { "first": { "$state": "/form/firstName" } } }`
63+
64+
Register functions via the `functions` prop on `JSONUIProvider` or `createRenderer`. See [Computed Values](/docs/computed-values) for details.
65+
66+
### New: State Watchers
67+
68+
Elements can declare a `watch` field to trigger actions when state values change. Useful for cascading dependencies like country/city selects.
69+
70+
```json
71+
{
72+
"type": "Select",
73+
"props": { "value": { "$bindState": "/form/country" }, "options": ["US", "Canada"] },
74+
"watch": {
75+
"/form/country": { "action": "loadCities", "params": { "country": { "$state": "/form/country" } } }
76+
}
77+
}
78+
```
79+
80+
`watch` is a top-level field on elements (sibling of type/props/children), not inside props. Watchers only fire on value changes, not on initial render. See [Watchers](/docs/watchers) for details.
81+
82+
### New: Cross-Field Validation
83+
84+
New built-in validation functions for cross-field comparisons:
85+
86+
- `equalTo` -- alias for `matches` with clearer semantics
87+
- `lessThan` -- value must be less than another field
88+
- `greaterThan` -- value must be greater than another field
89+
- `requiredIf` -- required only when a condition field is truthy
90+
91+
Validation check args now resolve through `resolvePropValue`, so `$state` expressions work consistently.
92+
93+
### New: `validateForm` Action
94+
95+
Built-in action (React) that validates all registered form fields at once and writes `{ valid, errors }` to state:
96+
97+
```json
98+
{
99+
"on": {
100+
"press": [
101+
{ "action": "validateForm", "params": { "statePath": "/formResult" } },
102+
{ "action": "submitForm" }
103+
]
104+
}
105+
}
106+
```
107+
108+
### Improved: shadcn/ui Validation
109+
110+
All form components now support `checks` and `validateOn` props:
111+
- Checkbox, Radio, Switch added validation support
112+
- `validateOn` controls timing: `"change"` (default for Select, Checkbox, Radio, Switch), `"blur"` (default for Input, Textarea), or `"submit"`
113+
114+
### New Examples
115+
116+
- **Vue example** -- standalone Vue 3 app with custom components
117+
- **Vite Renderers** -- side-by-side React and Vue renderers with shared catalog
118+
119+
---
120+
8121
## v0.9.1
9122

10123
February 2026

apps/web/app/(main)/docs/installation/page.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ If you want to wire json-render to an existing state management library instead
4747

4848
<PackageInstall packages="@json-render/jotai" />
4949

50+
<PackageInstall packages="@json-render/xstate" />
51+
5052
See the [Data Binding](/docs/data-binding#external-store-controlled-mode) guide for usage.
5153

5254
## For AI Integration

apps/web/lib/page-titles.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ export const PAGE_TITLES: Record<string, string> = {
2323
"docs/streaming": "Streaming",
2424
"docs/validation": "Validation",
2525
"docs/data-binding": "Data Binding",
26+
"docs/computed-values": "Computed Values",
2627
"docs/visibility": "Visibility",
28+
"docs/watchers": "Watchers",
2729
"docs/generation-modes": "Generation Modes",
2830
"docs/code-export": "Code Export",
2931
"docs/custom-schema": "Custom Schema & Renderer",

0 commit comments

Comments
 (0)