import { pageMetadata } from "@/lib/page-metadata"
export const metadata = pageMetadata("docs/migration")
# Migration Guide
This guide covers breaking changes introduced in v0.6.0 and how to update your code.
## State Provider
`DataProvider` has been renamed to `StateProvider`, and its props have changed.
**Before:**
```tsx
import { DataProvider } from "@json-render/react";
{children}
```
**After:**
```tsx
import { StateProvider } from "@json-render/react";
console.log(path, value)}>
{children}
```
`StateProvider` now manages state internally. Use `useStateStore()` to access `get`, `set`, and `update`.
Before
After
DataProvider
StateProvider
data prop
initialState prop
getValue / setValue props
Removed (use useStateStore() hook for get / set)
useData
useStateStore
useDataValue
useStateValue
useDataBinding
useStateBinding (deprecated, use useBoundProp instead)
DataModel type
StateModel type
## Dynamic Expressions
All dynamic value expressions have been renamed to use `$state`, `$item`, and `$index`.
**Before:**
```json
{
"type": "Text",
"props": {
"label": { "$path": "/user/name" },
"count": { "$data": "/items/length" }
}
}
```
**After:**
```json
{
"type": "Text",
"props": {
"label": { "$state": "/user/name" },
"count": { "$state": "/items/length" }
}
}
```
Inside repeat scopes, use `$item` and `$index`:
```json
{
"type": "Card",
"props": {
"title": { "$item": "name" },
"subtitle": { "$index": true }
}
}
```
Before
After
{'{ "$path": "/..." }'}
{'{ "$state": "/..." }'}
{'{ "$data": "/..." }'}
{'{ "$state": "/..." }'}
## Two-Way Binding
Form components no longer use `valuePath` / `statePath` props. Instead, use `$bindState` expressions on the value prop, and `useBoundProp` in your registry.
**Before (catalog):**
```typescript
Input: {
props: z.object({
label: z.string(),
valuePath: z.string(),
placeholder: z.string().optional(),
}),
}
```
**Before (spec):**
```json
{
"type": "Input",
"props": { "label": "Email", "valuePath": "/form/email" }
}
```
**Before (registry):**
```tsx
Input: ({ props }) => {
const [value, setValue] = useStateBinding(props.valuePath);
return setValue(e.target.value)} />;
}
```
**After (catalog):**
```typescript
Input: {
props: z.object({
label: z.string(),
value: z.string().optional(),
placeholder: z.string().optional(),
}),
}
```
**After (spec):**
```json
{
"type": "Input",
"props": { "label": "Email", "value": { "$bindState": "/form/email" } }
}
```
**After (registry):**
```tsx
Input: ({ props, bindings }) => {
const [value, setValue] = useBoundProp(props.value, bindings?.value);
return setValue(e.target.value)} />;
}
```
`$bindState` reads from and writes to the given state path. Inside repeat scopes, use `$bindItem` to bind to a field on the current item:
```json
{
"type": "Checkbox",
"props": { "checked": { "$bindItem": "completed" } }
}
```
## Visibility Conditions
Visibility conditions have been renamed to use `$state`, `$and`, and `$or`.
**Before:**
```json
{ "path": "/isAdmin" }
{ "eq": [{ "path": "/role" }, "admin"] }
{ "and": [{ "path": "/isAdmin" }, { "path": "/feature" }] }
{ "or": [{ "path": "/roleA" }, { "path": "/roleB" }] }
```
**After:**
```json
{ "$state": "/isAdmin" }
{ "$state": "/role", "eq": "admin" }
{ "$and": [{ "$state": "/isAdmin" }, { "$state": "/feature" }] }
{ "$or": [{ "$state": "/roleA" }, { "$state": "/roleB" }] }
```
You can also use an array as shorthand for `$and`:
```json
[{ "$state": "/isAdmin" }, { "$state": "/feature" }]
```
Inside repeat scopes, use `$item` and `$index`:
```json
{ "$item": "isActive" }
{ "$index": true, "eq": 0 }
```
## Event System
Components now use `emit` to fire named events. `onAction` has been removed.
**Before:**
```tsx
Button: ({ props, onAction }) => (
)
```
**After:**
```tsx
Button: ({ props, emit }) => (
)
```
`emit` is always defined (never `undefined`), so optional chaining is not needed.
## Actions Context
`dispatch` has been renamed to `execute`, and the provider prop has been renamed from `actionHandlers` to `handlers`.
**Before:**
```tsx
const { dispatch } = useActions();
dispatch({ action: "submit", params: {} });
```
**After:**
```tsx
const { execute } = useActions();
execute({ action: "submit", params: {} });
```
## Repeat / List Rendering
The `repeat` field now uses `statePath` instead of `path`.
**Before:**
```json
{
"type": "Column",
"repeat": { "path": "/todos", "key": "id" },
"children": ["todo-item"]
}
```
**After:**
```json
{
"type": "Column",
"repeat": { "statePath": "/todos", "key": "id" },
"children": ["todo-item"]
}
```
## Catalog Creation
`createCatalog` and `generateSystemPrompt` have been replaced by `defineSchema` + `defineCatalog`.
**Before:**
```typescript
import { createCatalog, generateSystemPrompt } from "@json-render/core";
const catalog = createCatalog({
name: "my-app",
components: { /* ... */ },
actions: { /* ... */ },
});
const prompt = generateSystemPrompt(catalog);
```
**After:**
```typescript
import { defineCatalog } from "@json-render/core";
import { schema } from "@json-render/react/schema";
const catalog = defineCatalog(schema, {
components: { /* ... */ },
actions: { /* ... */ },
});
const prompt = catalog.prompt();
// Chat mode prompt
const chatPrompt = catalog.prompt({ mode: "chat" });
```
## Validation
`ValidationCheck` now uses `type` instead of `fn`, `ValidationProvider` uses `customFunctions` instead of `functions`, and `useFieldValidation` takes a config object instead of a checks array.
**Before:**
```json
{ "fn": "required", "message": "Required" }
{ "fn": "minLength", "args": { "length": 8 }, "message": "Too short" }
```
**After:**
```json
{ "type": "required", "message": "Required" }
{ "type": "minLength", "args": { "min": 8 }, "message": "Too short" }
```
Before
After
{'{ fn: "required" }'}
{'{ type: "required" }'}
{'ValidationProvider functions={...}'}
{'ValidationProvider customFunctions={...}'}
useFieldValidation(path, checks)
useFieldValidation(path, config) where config is {'{ checks, validateOn? }'}
## Visibility Provider
The `auth` prop has been removed from `VisibilityProvider`. Auth state should be modeled as regular state.
**Before:**
```tsx
```
```json
{ "auth": "signedIn" }
```
**After:**
```tsx
```
```json
{ "$state": "/auth/isSignedIn" }
```
## Codegen
`traverseTree` has been renamed to `traverseSpec`, `SpecVisitor` to `TreeVisitor`, and the visitor callback now receives a `key` parameter.
**Before:**
```typescript
import { traverseTree } from "@json-render/codegen";
traverseTree(tree, (element) => {
// ...
});
```
**After:**
```typescript
import { traverseSpec } from "@json-render/codegen";
traverseSpec(spec, (element, key) => {
// ...
});
```
## Action Params
Action params in specs now use `statePath` instead of `path`.
**Before:**
```json
{
"on": {
"press": { "action": "setState", "params": { "path": "/count", "value": 0 } }
}
}
```
**After:**
```json
{
"on": {
"press": { "action": "setState", "params": { "statePath": "/count", "value": 0 } }
}
}
```
## Removed Exports
The following exports have been removed from `@json-render/core`:
Removed
Replacement
createCatalog
defineCatalog(schema, config)
generateCatalogPrompt
catalog.prompt()
generateSystemPrompt
catalog.prompt()
ComponentDefinition
Use catalog component config directly
CatalogConfig
Use defineCatalog parameters
SystemPromptOptions
Use PromptOptions
LogicExpression
Use VisibilityCondition
AuthState
Model auth as regular state (e.g. /auth/isSignedIn)