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
DataProviderStateProvider
data propinitialState prop
getValue / setValue propsRemoved (use useStateStore() hook for get / set)
useDatauseStateStore
useDataValueuseStateValue
useDataBindinguseStateBinding (deprecated, use useBoundProp instead)
DataModel typeStateModel 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)
evaluateLogicExpression Use evaluateVisibility
createRendererFromCatalog Use defineRegistry
traverseTree (codegen) Use traverseSpec