Skip to content
Merged
Show file tree
Hide file tree
Changes from 56 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
5f75453
react native
ctate Feb 8, 2026
18ea9e7
fixes
ctate Feb 8, 2026
8523440
pop/push
ctate Feb 8, 2026
e39ba68
fixes
ctate Feb 8, 2026
d17c729
rename data -> state
ctate Feb 8, 2026
047df9e
remove . tsbuildinfo
ctate Feb 8, 2026
58c59b7
fixes
ctate Feb 8, 2026
f776f14
fixes
ctate Feb 8, 2026
aa62208
user prompt
ctate Feb 8, 2026
d26564c
fixes
ctate Feb 8, 2026
185ebdc
$id
ctate Feb 8, 2026
23ac3cd
combine catalog.ts
ctate Feb 8, 2026
c426040
better actions
ctate Feb 8, 2026
056710c
repeat
ctate Feb 8, 2026
7ed0772
fix docs
ctate Feb 8, 2026
b80f210
fixes
ctate Feb 8, 2026
f18153f
fixes
ctate Feb 8, 2026
342f78c
fixes
ctate Feb 8, 2026
11adca2
better stream
ctate Feb 8, 2026
f7c1eaa
catalog
ctate Feb 8, 2026
0cb9dd4
fixes
ctate Feb 8, 2026
bad0b5b
fixes
ctate Feb 8, 2026
aa72110
fixes
ctate Feb 8, 2026
2afe371
dialog
ctate Feb 9, 2026
a4d7a15
sonner
ctate Feb 9, 2026
3986cf4
accordion
ctate Feb 9, 2026
ecf2396
catalog on homepage
ctate Feb 9, 2026
75eaf1e
fixes
ctate Feb 9, 2026
c3c34d4
more catalog
ctate Feb 9, 2026
895eaed
fixes
ctate Feb 9, 2026
46acfac
fixes
ctate Feb 9, 2026
a60552a
refactor
ctate Feb 9, 2026
0d89afe
mv
ctate Feb 9, 2026
08113f2
404
ctate Feb 9, 2026
f2a36a0
fixes
ctate Feb 9, 2026
d821835
nested
ctate Feb 9, 2026
5a89d42
fixes
ctate Feb 9, 2026
36242cd
fixes
ctate Feb 9, 2026
59dfdfc
fixes
ctate Feb 9, 2026
dadaa00
fixes
ctate Feb 9, 2026
1d8f476
mobile playground
ctate Feb 9, 2026
1d86165
mdx
ctate Feb 9, 2026
34afc7d
fix mdx
ctate Feb 9, 2026
847b982
fixes
ctate Feb 9, 2026
a17f818
streamdown
ctate Feb 9, 2026
83f33e3
fixes
ctate Feb 9, 2026
13a01aa
use haiku
ctate Feb 9, 2026
ec51ddf
fixes
ctate Feb 9, 2026
46b549e
great
ctate Feb 9, 2026
6b7fdd9
fixes
ctate Feb 9, 2026
52aaa30
fix ...
ctate Feb 9, 2026
ffcb188
fixes
ctate Feb 9, 2026
3a487f0
fixes
ctate Feb 9, 2026
16b64e6
fix build
ctate Feb 9, 2026
ef3e2db
fix lint
ctate Feb 9, 2026
11bfd10
fix lint
ctate Feb 9, 2026
2e72fdb
fix lint
ctate Feb 9, 2026
25001ac
fix tests
ctate Feb 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
[
"@json-render/core",
"@json-render/react",
"@json-render/react-native",
"@json-render/remotion",
"@json-render/codegen"
]
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@ coverage
# Vercel
.vercel

# Expo
.expo/

# Build Outputs
.next/
out/
build
dist
*.tsbuildinfo


# Debug
Expand Down
32 changes: 27 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,9 @@ function Dashboard({ spec }) {

| Package | Description |
|---------|-------------|
| `@json-render/core` | Schemas, catalogs, AI prompts, SpecStream utilities |
| `@json-render/core` | Schemas, catalogs, AI prompts, dynamic props, SpecStream utilities |
| `@json-render/react` | React renderer, contexts, hooks |
| `@json-render/react-native` | React Native renderer with standard mobile components |
| `@json-render/remotion` | Remotion video renderer, timeline schema |

## Renderers
Expand Down Expand Up @@ -197,18 +198,39 @@ const systemPrompt = catalog.prompt();
}
```

### Data Binding
### Dynamic Props

Any prop value can be data-driven using expressions:

```json
{
"type": "Metric",
"type": "Icon",
"props": {
"label": "Revenue",
"value": "{{data.revenue}}"
"name": { "$cond": { "eq": [{ "path": "/activeTab" }, "home"] }, "$then": "home", "$else": "home-outline" },
"color": { "$cond": { "eq": [{ "path": "/activeTab" }, "home"] }, "$then": "#007AFF", "$else": "#8E8E93" }
}
}
```

Two expression forms:

- **`{ "$path": "/state/key" }`** -- reads a value from the data model
- **`{ "$cond": <condition>, "$then": <value>, "$else": <value> }`** -- evaluates a condition (same syntax as visibility conditions) and picks a branch

### Actions

Components can trigger actions, including the built-in `setState` action:

```json
{
"type": "Pressable",
"props": { "action": "setState", "actionParams": { "path": "/activeTab", "value": "home" } },
"children": ["home-icon"]
}
```

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

---

## Demo
Expand Down
Original file line number Diff line number Diff line change
@@ -1,49 +1,25 @@
import Link from "next/link";
import { Code } from "@/components/code";
export const metadata = { title: "A2UI Integration" }

export const metadata = {
title: "A2UI Integration | json-render",
};
# A2UI Integration

Use `@json-render/core` to support [A2UI](https://a2ui.org) natively.

<div className="rounded-lg border border-amber-500/50 bg-amber-500/10 p-4 mb-8">
<p className="text-sm text-amber-700 dark:text-amber-300">
<strong>Concept:</strong> This page demonstrates how json-render can support A2UI. The examples are illustrative and may require adaptation for production use.
</p>
</div>

## Native A2UI Support

`@json-render/core` is schema-agnostic. Define a catalog that matches A2UI's format and build a renderer that understands it - no conversion layer needed.

## Example A2UI Message

export default function A2UIPage() {
return (
<article>
<h1 className="text-3xl font-bold mb-4">A2UI Integration</h1>
<p className="text-muted-foreground mb-8">
Use <code className="text-foreground">@json-render/core</code> to
support{" "}
<a
href="https://a2ui.org"
target="_blank"
rel="noopener noreferrer"
className="text-foreground hover:underline"
>
A2UI
</a>{" "}
natively.
</p>

<div className="rounded-lg border border-amber-500/50 bg-amber-500/10 p-4 mb-8">
<p className="text-sm text-amber-700 dark:text-amber-300">
<strong>Concept:</strong> This page demonstrates how json-render can
support A2UI. The examples are illustrative and may require adaptation
for production use.
</p>
</div>

<h2 className="text-xl font-semibold mt-12 mb-4">Native A2UI Support</h2>
<p className="text-sm text-muted-foreground mb-4">
<code className="text-foreground">@json-render/core</code> is
schema-agnostic. Define a catalog that matches A2UI&apos;s format and
build a renderer that understands it - no conversion layer needed.
</p>

<h2 className="text-xl font-semibold mt-12 mb-4">Example A2UI Message</h2>
<p className="text-sm text-muted-foreground mb-4">
A2UI uses an adjacency list model - a flat list of components with ID
references. This makes it easy to patch individual components:
</p>
<Code lang="json">{`{
A2UI uses an adjacency list model - a flat list of components with ID references. This makes it easy to patch individual components:

```json
{
"surfaceUpdate": {
"surfaceId": "main",
"components": [
Expand Down Expand Up @@ -83,12 +59,13 @@ export default function A2UIPage() {
}
]
}
}`}</Code>
}
```

## Define the A2UI Catalog

<h2 className="text-xl font-semibold mt-12 mb-4">
Define the A2UI Catalog
</h2>
<Code lang="typescript">{`import { createCatalog } from '@json-render/core';
```typescript
import { createCatalog } from '@json-render/core';
import { z } from 'zod';

// A2UI BoundValue schema
Expand Down Expand Up @@ -151,15 +128,15 @@ export const a2uiCatalog = createCatalog({
},
// Add more A2UI standard components...
},
});`}</Code>
});
```

## Define the A2UI Schema

<h2 className="text-xl font-semibold mt-12 mb-4">
Define the A2UI Schema
</h2>
<p className="text-sm text-muted-foreground mb-4">
Define the schema for A2UI message types:
</p>
<Code lang="typescript">{`import { z } from 'zod';
Define the schema for A2UI message types:

```typescript
import { z } from 'zod';

// Component instance in the adjacency list
const A2UIComponent = z.object({
Expand All @@ -173,8 +150,8 @@ const SurfaceUpdate = z.object({
components: z.array(A2UIComponent),
});

// Data model update message
const DataModelUpdate = z.object({
// State model update message
const StateModelUpdate = z.object({
surfaceId: z.string().optional(),
path: z.string().optional(),
contents: z.array(z.object({
Expand All @@ -196,18 +173,18 @@ const BeginRendering = z.object({
// Complete A2UI message schema
export const A2UIMessage = z.object({
surfaceUpdate: SurfaceUpdate.optional(),
dataModelUpdate: DataModelUpdate.optional(),
dataModelUpdate: StateModelUpdate.optional(),
beginRendering: BeginRendering.optional(),
deleteSurface: z.object({ surfaceId: z.string() }).optional(),
});`}</Code>
});
```

## Build an A2UI Renderer

Create a renderer that processes the A2UI adjacency list format:

<h2 className="text-xl font-semibold mt-12 mb-4">
Build an A2UI Renderer
</h2>
<p className="text-sm text-muted-foreground mb-4">
Create a renderer that processes the A2UI adjacency list format:
</p>
<Code lang="tsx">{`import { a2uiCatalog } from './catalog';
```tsx
import { a2uiCatalog } from './catalog';

// Component registry
const components = {
Expand Down Expand Up @@ -239,7 +216,7 @@ export function renderA2UI(
if (!bound) return undefined;
if (bound.literalString) return bound.literalString;
if (bound.path) {
const parts = bound.path.replace(/^\\//, '').split('/');
const parts = bound.path.replace(/^\//, '').split('/');
let value = dataModel;
for (const p of parts) value = value?.[p];
return value;
Expand Down Expand Up @@ -272,10 +249,13 @@ export function renderA2UI(
}

return render(rootId);
}`}</Code>
}
```

## Usage

<h2 className="text-xl font-semibold mt-12 mb-4">Usage</h2>
<Code lang="tsx">{`const [components] = useState(() => new Map());
```tsx
const [components] = useState(() => new Map());
const [dataModel, setDataModel] = useState({});
const [rootId, setRootId] = useState<string | null>(null);

Expand All @@ -295,19 +275,9 @@ function handleMessage(msg: any) {
}

// Render
{rootId && renderA2UI(components, dataModel, rootId, handleAction)}`}</Code>

<h2 className="text-xl font-semibold mt-12 mb-4">Next</h2>
<p className="text-sm text-muted-foreground">
Learn about{" "}
<Link
href="/docs/adaptive-cards"
className="text-foreground hover:underline"
>
Adaptive Cards integration
</Link>{" "}
for another UI protocol.
</p>
</article>
);
}
{rootId && renderA2UI(components, dataModel, rootId, handleAction)}
```

## Next

Learn about [Adaptive Cards integration](/docs/adaptive-cards) for another UI protocol.
Loading
Loading