; // Map of prop names to state paths (for $bindState)
loading?: boolean; // True while spec is streaming
}
```
```svelte
```
```svelte
{props.title}
{#if children}
{@render children()}
{/if}
```
## Creating a Registry
```typescript
import { defineRegistry } from "@json-render/svelte";
import { catalog } from "./catalog";
import Card from "./components/Card.svelte";
import Button from "./components/Button.svelte";
const { registry, handlers, executeAction } = defineRegistry(catalog, {
components: {
Card,
Button,
},
actions: {
submit: async (params, setState, state) => {
// handle action
},
},
});
```
## Spec Structure (Element Tree)
The Svelte schema uses the element tree format:
```json
{
"root": "card1",
"elements": {
"card1": {
"type": "Card",
"props": { "title": "Hello" },
"children": ["btn1"]
},
"btn1": {
"type": "Button",
"props": { "label": "Click me" }
}
}
}
```
## Visibility Conditions
Use `visible` on elements to show/hide based on state:
- `{ "$state": "/path" }` - truthy check
- `{ "$state": "/path", "eq": value }` - equality check
- `{ "$state": "/path", "not": true }` - falsy check
- `{ "$and": [cond1, cond2] }` - AND conditions
- `{ "$or": [cond1, cond2] }` - OR conditions
## Providers (via JsonUIProvider)
`JsonUIProvider` composes all contexts. Individual contexts:
| Context | Purpose |
| ------------------- | -------------------------------------------------- |
| `StateContext` | Share state across components (JSON Pointer paths) |
| `ActionContext` | Handle actions dispatched via the event system |
| `VisibilityContext` | Enable conditional rendering based on state |
| `ValidationContext` | Form field validation |
## Event System
Components use `emit` to fire named events. The element's `on` field maps events to action bindings:
```svelte
```
```json
{
"type": "Button",
"props": { "label": "Submit" },
"on": { "press": { "action": "submit" } }
}
```
## Built-in Actions
The `setState` action is handled automatically and updates the state model:
```json
{
"action": "setState",
"actionParams": { "statePath": "/activeTab", "value": "home" }
}
```
Other built-in actions: `pushState`, `removeState`, `push`, `pop`.
## Dynamic Props and Two-Way Binding
Expression forms resolved before your component receives props:
- `{"$state": "/state/key"}` - read from state
- `{"$bindState": "/form/email"}` - read + write-back to state
- `{"$bindItem": "field"}` - read + write-back for repeat items
- `{"$cond": , "$then": , "$else": }` - conditional value
For writable bindings inside components, use `getBoundProp`:
```svelte
```
## Context Helpers
Preferred helpers:
- `getStateValue(path)` - returns `{ current }` (read/write)
- `getBoundProp(() => value, () => bindingPath)` - returns `{ current }` (read/write when bound)
- `isVisible(condition)` - returns `{ current }` (boolean)
- `getAction(name)` - returns `{ current }` (registered handler)
Advanced context access:
- `getStateContext()`
- `getActionContext()`
- `getVisibilityContext()`
- `getValidationContext()`
- `getOptionalValidationContext()`
- `getFieldValidation(ctx, path, config?)`
## Streaming UI
Use `createUIStream` for spec streaming:
```svelte
{#if stream.spec}
{/if}
```
Use `createChatUI` for chat + UI responses:
```typescript
const chat = createChatUI({ api: "/api/chat-ui" });
await chat.send("Build a settings panel");
```