page.mdx 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. import { pageMetadata } from "@/lib/page-metadata"
  2. export const metadata = pageMetadata("docs/changelog")
  3. # Changelog
  4. Notable changes and updates to json-render.
  5. ## v0.8.0
  6. February 2026
  7. ### New: External State Store
  8. The `StateStore` interface lets you plug in your own state management (Redux, Zustand, Jotai, XState, etc.) instead of the built-in internal store. Pass a `store` prop to `StateProvider`, `JSONUIProvider`, or `createRenderer` for controlled mode.
  9. New adapter packages: `@json-render/redux`, `@json-render/zustand`, `@json-render/jotai`.
  10. ### Changed: `onStateChange` signature updated (breaking)
  11. The `onStateChange` callback now receives a single array of changed entries instead of being called once per path. This makes batch updates via `update()` easier to handle:
  12. ```ts
  13. // Before
  14. onStateChange?: (path: string, value: unknown) => void
  15. // After
  16. onStateChange?: (changes: Array<{ path: string; value: unknown }>) => void
  17. ```
  18. The callback is only called when a `set()` or `update()` call actually changes the state. A `set()` call produces a single-element array; an `update()` call produces one array with all changed paths.
  19. ### New: `@json-render/react-pdf`
  20. PDF renderer for json-render, powered by [`@react-pdf/renderer`](https://react-pdf.org/). Define catalogs and registries the same way as `@json-render/react`, but output PDF documents instead of web UI.
  21. ```bash
  22. npm install @json-render/core @json-render/react-pdf
  23. ```
  24. ```typescript
  25. import { renderToBuffer } from "@json-render/react-pdf";
  26. import type { Spec } from "@json-render/core";
  27. const spec: Spec = {
  28. root: "doc",
  29. elements: {
  30. doc: { type: "Document", props: { title: "Invoice" }, children: ["page"] },
  31. page: {
  32. type: "Page",
  33. props: { size: "A4" },
  34. children: ["heading", "table"],
  35. },
  36. heading: {
  37. type: "Heading",
  38. props: { text: "Invoice #1234", level: "h1" },
  39. children: [],
  40. },
  41. table: {
  42. type: "Table",
  43. props: {
  44. columns: [
  45. { header: "Item", width: "60%" },
  46. { header: "Price", width: "40%", align: "right" },
  47. ],
  48. rows: [
  49. ["Widget A", "$10.00"],
  50. ["Widget B", "$25.00"],
  51. ],
  52. },
  53. children: [],
  54. },
  55. },
  56. };
  57. const buffer = await renderToBuffer(spec);
  58. ```
  59. Server-side rendering APIs:
  60. - `renderToBuffer(spec)` -- render to an in-memory PDF buffer
  61. - `renderToStream(spec)` -- render to a readable stream (pipe to HTTP response)
  62. - `renderToFile(spec, path)` -- render directly to a file
  63. 15 standard components covering document structure (Document, Page), layout (View, Row, Column), content (Heading, Text, Image, Link), data (Table, List), decorative (Divider, Spacer), and page-level (PageNumber).
  64. Supports custom catalogs with `defineRegistry`, server-safe imports via `@json-render/react-pdf/server`, and full context support (state, visibility, actions, validation, repeat scopes).
  65. ---
  66. ## v0.7.0
  67. February 2026
  68. ### New: `@json-render/shadcn`
  69. Pre-built [shadcn/ui](https://ui.shadcn.com/) component library for json-render. 36 components built on Radix UI + Tailwind CSS, ready to use with `defineCatalog` and `defineRegistry`.
  70. ```bash
  71. npm install @json-render/shadcn
  72. ```
  73. ```typescript
  74. import { defineCatalog } from "@json-render/core";
  75. import { schema } from "@json-render/react/schema";
  76. import { shadcnComponentDefinitions } from "@json-render/shadcn/catalog";
  77. import { defineRegistry } from "@json-render/react";
  78. import { shadcnComponents } from "@json-render/shadcn";
  79. const catalog = defineCatalog(schema, {
  80. components: {
  81. Card: shadcnComponentDefinitions.Card,
  82. Button: shadcnComponentDefinitions.Button,
  83. Input: shadcnComponentDefinitions.Input,
  84. },
  85. actions: {},
  86. });
  87. const { registry } = defineRegistry(catalog, {
  88. components: {
  89. Card: shadcnComponents.Card,
  90. Button: shadcnComponents.Button,
  91. Input: shadcnComponents.Input,
  92. },
  93. });
  94. ```
  95. Components include: layout (Card, Stack, Grid, Separator), navigation (Tabs, Accordion, Collapsible, Pagination), overlay (Dialog, Drawer, Tooltip, Popover, DropdownMenu), content (Heading, Text, Image, Avatar, Badge, Alert, Carousel, Table), feedback (Progress, Skeleton, Spinner), and input (Button, Link, Input, Textarea, Select, Checkbox, Radio, Switch, Slider, Toggle, ToggleGroup, ButtonGroup).
  96. See the [API reference](/docs/api/shadcn) for full details.
  97. ### New: Event Handles (`on()`)
  98. Components now receive an `on(event)` function in addition to `emit(event)`. The `on()` function returns an `EventHandle` with metadata:
  99. - `emit()` -- fire the event
  100. - `shouldPreventDefault` -- whether any action binding requested `preventDefault`
  101. - `bound` -- whether any handler is bound to this event
  102. ```tsx
  103. Link: ({ props, on }) => {
  104. const click = on("click");
  105. return (
  106. <a href={props.href} onClick={(e) => {
  107. if (click.shouldPreventDefault) e.preventDefault();
  108. click.emit();
  109. }}>{props.label}</a>
  110. );
  111. },
  112. ```
  113. ### New: `BaseComponentProps`
  114. Catalog-agnostic base type for component render functions. Use when building reusable component libraries (like `@json-render/shadcn`) that are not tied to a specific catalog.
  115. ```typescript
  116. import type { BaseComponentProps } from "@json-render/react";
  117. const Card = ({ props, children }: BaseComponentProps<{ title?: string }>) => (
  118. <div>{props.title}{children}</div>
  119. );
  120. ```
  121. ### New: Built-in Actions in Schema
  122. Schemas can now declare `builtInActions` -- actions that are always available at runtime and automatically injected into prompts. The React schema declares `setState`, `pushState`, and `removeState` as built-in, so they appear in prompts without needing to be listed in catalog `actions`.
  123. ### New: `preventDefault` on `ActionBinding`
  124. Action bindings now support a `preventDefault` boolean field, allowing the LLM to request that default browser behavior (e.g. navigation on links) be prevented.
  125. ### Improved: Stream Transform Text Block Splitting
  126. `createJsonRenderTransform()` now properly splits text blocks around spec data by emitting `text-end`/`text-start` pairs. This ensures the AI SDK creates separate text parts, preserving correct interleaving of prose and UI in `message.parts`.
  127. ### Improved: `defineRegistry` Actions Requirement
  128. `defineRegistry` now conditionally requires the `actions` field only when the catalog declares actions. Catalogs with no actions (e.g. `actions: {}`) no longer need to pass an empty actions object.
  129. ---
  130. ## v0.6.0
  131. February 2026
  132. ### New: Chat Mode (Inline GenUI)
  133. json-render now supports two generation modes: **Generate** (JSONL-only, the default) and **Chat** (text + JSONL inline). Chat mode lets the AI respond conversationally with embedded UI specs, ideal for chatbots and copilot experiences.
  134. ```typescript
  135. // Generate mode (default) — AI outputs only JSONL
  136. const prompt = catalog.prompt();
  137. // Chat mode — AI outputs text + JSONL inline
  138. const chatPrompt = catalog.prompt({ mode: "chat" });
  139. ```
  140. On the server, `pipeJsonRender()` separates text from JSONL patches in a mixed stream:
  141. ```typescript
  142. import { pipeJsonRender } from "@json-render/core";
  143. import { createUIMessageStream, createUIMessageStreamResponse } from "ai";
  144. const stream = createUIMessageStream({
  145. execute: async ({ writer }) => {
  146. writer.merge(pipeJsonRender(result.toUIMessageStream()));
  147. },
  148. });
  149. return createUIMessageStreamResponse({ stream });
  150. ```
  151. On the client, `useJsonRenderMessage` extracts the spec and text from message parts:
  152. ```tsx
  153. import { useJsonRenderMessage } from "@json-render/react";
  154. function ChatMessage({ message }) {
  155. const { spec, text, hasSpec } = useJsonRenderMessage(message.parts);
  156. return (
  157. <div>
  158. {text && <Markdown>{text}</Markdown>}
  159. {hasSpec && <Renderer spec={spec} registry={registry} />}
  160. </div>
  161. );
  162. }
  163. ```
  164. ### New: AI SDK Integration
  165. First-class Vercel AI SDK support with typed data parts and stream utilities.
  166. - `SpecDataPart` type for `data-spec` stream parts (patch, flat, nested payloads)
  167. - `SPEC_DATA_PART` / `SPEC_DATA_PART_TYPE` constants for type-safe part filtering
  168. - `createJsonRenderTransform()` low-level TransformStream for custom pipelines
  169. - `createMixedStreamParser()` for parsing mixed text + JSONL streams
  170. ### New: Two-Way Binding
  171. Props can now use `$bindState` and `$bindItem` expressions for two-way data binding. The renderer resolves bindings and passes a `bindings` map to components, enabling write-back to state without custom `valuePath` props.
  172. ```json
  173. {
  174. "type": "Input",
  175. "props": { "label": "Email", "value": { "$bindState": "/form/email" } }
  176. }
  177. ```
  178. ```tsx
  179. import { useBoundProp } from "@json-render/react";
  180. Input: ({ props, bindings }) => {
  181. const [value, setValue] = useBoundProp<string>(props.value, bindings?.value);
  182. return <input value={value ?? ""} onChange={(e) => setValue(e.target.value)} />;
  183. }
  184. ```
  185. ### New: Expression-Based Props and Visibility
  186. All dynamic expressions now use structured `$state`, `$item`, and `$index` objects instead of string token rewriting. This is simpler, more explicit, and works for both props and visibility conditions.
  187. **Props:**
  188. ```json
  189. { "title": { "$state": "/user/name" } }
  190. { "label": { "$item": "title" } }
  191. { "position": { "$index": true } }
  192. ```
  193. **Visibility:**
  194. ```json
  195. { "$state": "/isAdmin" }
  196. { "$state": "/role", "eq": "admin" }
  197. [{ "$state": "/isAdmin" }, { "$state": "/feature" }]
  198. { "$or": [{ "$state": "/roleA" }, { "$state": "/roleB" }] }
  199. { "$item": "isActive" }
  200. { "$index": true, "gt": 0 }
  201. ```
  202. Comparison operators: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `not`.
  203. ### New: React Chat Hooks
  204. - `useChatUI()` — full chat hook with message history, streaming, and spec extraction
  205. - `useJsonRenderMessage()` — extract spec + text from a message's parts array
  206. - `buildSpecFromParts()` / `getTextFromParts()` — utilities for working with AI SDK message parts
  207. - `useBoundProp()` — two-way binding hook for `$bindState` / `$bindItem`
  208. ### New: Chat Example
  209. Full-featured chat example (`examples/chat`) with AI agent, tool calls (crypto, GitHub, Hacker News, weather, search), theme toggle, and streaming inline UI generation.
  210. ### Improved: Renderer Performance
  211. - `ElementRenderer` is now `React.memo`'d for better performance with repeat lists
  212. - `emit` is always defined (never `undefined`)
  213. - Repeat scope passes the actual item object, eliminating string token rewriting
  214. ### Improved: Utilities
  215. - `applySpecPatch()` — typed wrapper for applying a single patch to a Spec
  216. - `nestedToFlat()` — convert nested tree specs to flat format
  217. - `resolveBindings()` / `resolveActionParam()` — resolve binding paths and action params
  218. ### Breaking Changes
  219. - `{ $path }` and `{ path }` replaced by `{ $state }`, `{ $item }`, `{ $index }` in props
  220. - Visibility: `{ path }` -> `{ $state }`, `{ and/or/not }` -> `{ $and/$or }` with `not` as operator flag
  221. - `DynamicValue`: `{ path: string }` -> `{ $state: string }`
  222. - `repeat.path` -> `repeat.statePath`
  223. - Action params: `path` -> `statePath` in setState action
  224. - `actionHandlers` -> `handlers` on `JSONUIProvider` / `ActionProvider`
  225. - `AuthState` and `{ auth }` visibility conditions removed (model auth as regular state)
  226. - Legacy catalog API removed: `createCatalog`, `generateCatalogPrompt`, `generateSystemPrompt`
  227. - React exports removed: `createRendererFromCatalog`, `rewriteRepeatTokens`
  228. - Codegen: `traverseTree` -> `traverseSpec`
  229. See the [Migration Guide](/docs/migration) for detailed upgrade instructions.
  230. ---
  231. ## v0.5.0
  232. February 2026
  233. ### New: @json-render/react-native
  234. Full React Native renderer with 25+ standard components, data binding, visibility, actions, and dynamic props. Build AI-generated native mobile UIs with the same catalog-driven approach as web.
  235. ```tsx
  236. import { defineCatalog } from "@json-render/core";
  237. import { schema } from "@json-render/react-native/schema";
  238. import {
  239. standardComponentDefinitions,
  240. standardActionDefinitions,
  241. } from "@json-render/react-native/catalog";
  242. import { defineRegistry, Renderer } from "@json-render/react-native";
  243. const catalog = defineCatalog(schema, {
  244. components: { ...standardComponentDefinitions },
  245. actions: standardActionDefinitions,
  246. });
  247. const { registry } = defineRegistry(catalog, { components: {} });
  248. <Renderer spec={spec} registry={registry} />
  249. ```
  250. Includes standard components for layout (Container, Row, Column, ScrollContainer, SafeArea, Pressable, Spacer, Divider), content (Heading, Paragraph, Label, Image, Avatar, Badge, Chip), input (Button, TextInput, Switch, Checkbox, Slider, SearchBar), feedback (Spinner, ProgressBar), and composite (Card, ListItem, Modal).
  251. ### New: Event System
  252. Components now use `emit` to fire named events instead of directly dispatching actions. The element's `on` field maps events to action bindings, decoupling component logic from action handling.
  253. ```tsx
  254. // Component emits a named event
  255. Button: ({ props, emit }) => (
  256. <button onClick={() => emit("press")}>{props.label}</button>
  257. ),
  258. // Element spec maps events to actions
  259. {
  260. "type": "Button",
  261. "props": { "label": "Submit" },
  262. "on": { "press": { "action": "submit", "params": { "formId": "main" } } }
  263. }
  264. ```
  265. ### New: Repeat/List Rendering
  266. Elements can now iterate over state arrays using the `repeat` field. Child elements use `{ "$item": "field" }` to read from the current item and `{ "$index": true }` for the current array index.
  267. ```json
  268. {
  269. "type": "Column",
  270. "repeat": { "statePath": "/posts", "key": "id" },
  271. "children": ["post-card"]
  272. }
  273. ```
  274. ```json
  275. {
  276. "type": "Card",
  277. "props": { "title": { "$item": "title" } }
  278. }
  279. ```
  280. ### New: User Prompt Builder
  281. Build structured user prompts with optional spec refinement and state context:
  282. ```typescript
  283. import { buildUserPrompt } from "@json-render/core";
  284. // Fresh generation
  285. buildUserPrompt({ prompt: "create a todo app" });
  286. // Refinement (patch-only mode)
  287. buildUserPrompt({ prompt: "add a toggle", currentSpec: spec });
  288. // With runtime state
  289. buildUserPrompt({ prompt: "show data", state: { todos: [] } });
  290. ```
  291. ### New: Spec Validation
  292. Validate spec structure and auto-fix common issues:
  293. ```typescript
  294. import { validateSpec, autoFixSpec } from "@json-render/core";
  295. const { valid, issues } = validateSpec(spec);
  296. const fixed = autoFixSpec(spec);
  297. ```
  298. ### Improved: State Management
  299. `DataProvider` has been renamed to `StateProvider` with a clearer API. State is now a first-class part of specs. Elements can bind to state via `$state` expressions, and the built-in `setState` action updates state directly.
  300. ### Improved: AI Prompts
  301. Schema prompts now include streaming best practices, repeat/list examples, and state patching guidance. Schemas can also define `defaultRules` that are always included in generated prompts.
  302. ### Improved: Documentation
  303. - All documentation pages migrated to MDX
  304. - AI-powered documentation chat
  305. - Dynamic Open Graph images for all docs pages
  306. - Improved playground
  307. ### Breaking Changes
  308. - `DataProvider` renamed to `StateProvider`
  309. - `useData` renamed to `useStateStore`, `useDataValue` to `useStateValue`, `useDataBinding` to `useStateBinding`
  310. - `onAction` renamed to `emit` in component context
  311. - `DataModel` type renamed to `StateModel`
  312. - `Action` type renamed to `ActionBinding` (old name still available but deprecated)
  313. ---
  314. ## v0.4.0
  315. February 2026
  316. ### New: Custom Schema System
  317. Create custom output formats with `defineSchema`. Each renderer now defines its own schema, enabling completely different spec formats for different use cases.
  318. ```typescript
  319. import { defineSchema } from "@json-render/core";
  320. const mySchema = defineSchema((s) => ({
  321. spec: s.object({
  322. pages: s.array(s.object({
  323. title: s.string(),
  324. blocks: s.array(s.ref("catalog.blocks")),
  325. })),
  326. }),
  327. catalog: s.object({
  328. blocks: s.map({ props: s.zod(), description: s.string() }),
  329. }),
  330. }), {
  331. promptTemplate: myPromptTemplate,
  332. });
  333. ```
  334. ### New: Component Slots
  335. Components can now define which slots they accept. Use `["default"]` for regular children, or named slots like `["header", "footer"]` for more complex layouts.
  336. ```typescript
  337. const catalog = defineCatalog(schema, {
  338. components: {
  339. Card: {
  340. props: z.object({ title: z.string() }),
  341. slots: ["default"], // accepts children
  342. description: "A card container",
  343. },
  344. Layout: {
  345. props: z.object({}),
  346. slots: ["header", "content", "footer"], // named slots
  347. description: "Page layout with header, content, footer",
  348. },
  349. },
  350. });
  351. ```
  352. ### New: AI Prompt Generation
  353. Catalogs now generate AI system prompts automatically with `catalog.prompt()`. The prompt includes all component definitions, props schemas, and action descriptions - ensuring the AI only generates valid specs.
  354. ```typescript
  355. import { defineCatalog } from "@json-render/core";
  356. import { schema } from "@json-render/react/schema";
  357. const catalog = defineCatalog(schema, {
  358. components: { /* ... */ },
  359. actions: { /* ... */ },
  360. });
  361. // Generate system prompt for AI
  362. const systemPrompt = catalog.prompt();
  363. // Use with any AI SDK
  364. const result = await streamText({
  365. model: "claude-haiku-4.5",
  366. system: systemPrompt,
  367. prompt: userMessage,
  368. });
  369. ```
  370. ### New: @json-render/remotion
  371. Generate AI-powered videos with Remotion. Define video catalogs, stream timeline specs, and render with the Remotion Player.
  372. ```tsx
  373. import { Player } from "@remotion/player";
  374. import { Renderer, schema, standardComponentDefinitions } from "@json-render/remotion";
  375. const catalog = defineCatalog(schema, {
  376. components: standardComponentDefinitions,
  377. transitions: standardTransitionDefinitions,
  378. });
  379. <Player
  380. component={Renderer}
  381. inputProps={{ spec }}
  382. durationInFrames={spec.composition.durationInFrames}
  383. fps={spec.composition.fps}
  384. compositionWidth={spec.composition.width}
  385. compositionHeight={spec.composition.height}
  386. />
  387. ```
  388. Includes 10 standard video components (TitleCard, TypingText, SplitScreen, etc.), 7 transition types, and the ClipWrapper utility for custom components.
  389. ### New: SpecStream
  390. SpecStream is json-render's streaming format for progressively building specs from JSONL patches. The new compiler API makes it easy to process streaming AI responses.
  391. ```typescript
  392. import { createSpecStreamCompiler } from "@json-render/core";
  393. const compiler = createSpecStreamCompiler<MySpec>();
  394. // Process streaming chunks
  395. const { result, newPatches } = compiler.push(chunk);
  396. setSpec(result); // Update UI with partial result
  397. ```
  398. ### Improved: Dashboard Example
  399. The dashboard example is now a full-featured accounting dashboard with:
  400. - Persistent SQLite database with Drizzle ORM
  401. - RESTful API for customers, invoices, expenses, accounts
  402. - Draggable widget reordering
  403. - AI-powered widget generation with streaming
  404. - Real data binding to database records
  405. ### Improved: Documentation
  406. - Interactive playground for testing specs
  407. - New guides: Custom Schema, Streaming, Code Export
  408. - Full API reference for all packages
  409. - Integration guides: A2UI, AG-UI, Adaptive Cards, OpenAPI
  410. ### Breaking Changes
  411. - `UITree` type renamed to `Spec`
  412. - Schema is now imported from renderer packages (`@json-render/react`) not core
  413. - `defineCatalog` now requires a schema as first argument
  414. ---
  415. ## v0.3.0
  416. January 2026
  417. Internal release with codegen foundations.
  418. - Added `@json-render/codegen` package (spec traversal and JSX serialization)
  419. - Configurable AI model via environment variables
  420. - Documentation improvements and bug fixes
  421. *Note: Only @json-render/core was published to npm for this release.*
  422. ---
  423. ## v0.2.0
  424. January 2026
  425. Initial public release.
  426. - Core catalog and spec types
  427. - React renderer with contexts for data, actions, visibility
  428. - AI prompt generation from catalogs
  429. - Basic streaming support
  430. - Dashboard example application