page.mdx 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. export const metadata = { title: "Specs" }
  2. # Specs
  3. A spec is a JSON document that describes your UI.
  4. ## What is a Spec?
  5. A spec (specification) is the actual JSON that describes a UI. It uses components from a [catalog](/docs/catalog) and can optionally follow a [schema](/docs/schemas). Specs can be:
  6. - Generated by AI in real-time
  7. - Stored in a database
  8. - Streamed progressively from a server
  9. - Hand-authored as JSON files
  10. json-render is schema-agnostic — your specs can follow any JSON structure you choose.
  11. ## Example Specs
  12. ### Simple Spec
  13. A basic spec using the `@json-render/react` schema. Note the flat structure with a `root` key and `elements` map:
  14. ```json
  15. {
  16. "root": "card-1",
  17. "elements": {
  18. "card-1": {
  19. "type": "Card",
  20. "props": { "title": "Welcome" },
  21. "children": ["text-1"]
  22. },
  23. "text-1": {
  24. "type": "Text",
  25. "props": { "content": { "$state": "/user/greeting" } },
  26. "children": []
  27. }
  28. }
  29. }
  30. ```
  31. ### Complex Spec
  32. A more complex spec with multiple nested elements:
  33. ```json
  34. {
  35. "root": "card-1",
  36. "elements": {
  37. "card-1": {
  38. "type": "Card",
  39. "props": { "title": "User Profile", "padding": "md" },
  40. "children": ["row-1", "button-1"]
  41. },
  42. "row-1": {
  43. "type": "Row",
  44. "props": { "gap": "md" },
  45. "children": ["avatar-1", "stack-1"]
  46. },
  47. "avatar-1": {
  48. "type": "Avatar",
  49. "props": { "src": { "$state": "/user/avatar" }, "alt": { "$state": "/user/name" } },
  50. "children": []
  51. },
  52. "stack-1": {
  53. "type": "Stack",
  54. "props": { "gap": "sm" },
  55. "children": ["name-text", "email-text"]
  56. },
  57. "name-text": {
  58. "type": "Text",
  59. "props": { "content": { "$state": "/user/name" }, "variant": "heading" },
  60. "children": []
  61. },
  62. "email-text": {
  63. "type": "Text",
  64. "props": { "content": { "$state": "/user/email" }, "variant": "caption" },
  65. "children": []
  66. },
  67. "button-1": {
  68. "type": "Button",
  69. "props": { "label": "Edit Profile" },
  70. "children": []
  71. }
  72. }
  73. }
  74. ```
  75. ### Block-Level Spec
  76. A high-level spec using semantic blocks for page layouts:
  77. ```json
  78. {
  79. "root": "page",
  80. "elements": {
  81. "page": {
  82. "type": "Page",
  83. "props": {},
  84. "children": ["header", "hero", "features", "footer"]
  85. },
  86. "header": {
  87. "type": "Header",
  88. "props": { "logo": "/logo.svg", "navItems": ["Products", "Pricing", "Docs"] },
  89. "children": []
  90. },
  91. "hero": {
  92. "type": "Hero",
  93. "props": {
  94. "title": "Build UIs with JSON",
  95. "subtitle": "Let AI generate your interfaces",
  96. "ctaLabel": "Get Started",
  97. "ctaHref": "/docs"
  98. },
  99. "children": []
  100. },
  101. "features": {
  102. "type": "Features",
  103. "props": { "columns": 3 },
  104. "children": ["feature-1", "feature-2", "feature-3"]
  105. },
  106. "feature-1": {
  107. "type": "Feature",
  108. "props": { "icon": "zap", "title": "Fast", "description": "Render UIs in milliseconds" },
  109. "children": []
  110. },
  111. "feature-2": {
  112. "type": "Feature",
  113. "props": { "icon": "shield", "title": "Secure", "description": "Validate all specs against your catalog" },
  114. "children": []
  115. },
  116. "feature-3": {
  117. "type": "Feature",
  118. "props": { "icon": "sparkles", "title": "AI-Ready", "description": "Generate prompts from your catalog" },
  119. "children": []
  120. },
  121. "footer": {
  122. "type": "Footer",
  123. "props": { "copyright": "2025 Acme Inc", "links": ["Privacy", "Terms", "Contact"] },
  124. "children": []
  125. }
  126. }
  127. }
  128. ```
  129. ## Spec Anatomy
  130. Specs are schema-agnostic — the JSON structure is entirely up to you. The examples below use the `root` + `elements` flat tree format from the `@json-render/react` schema, which is optimized for AI generation and streaming.
  131. ### Root and Elements
  132. In the React schema, a spec has a `root` key pointing to the entry element, and an `elements` map containing all elements:
  133. ```json
  134. {
  135. "root": "card-1",
  136. "elements": {
  137. "card-1": {
  138. "type": "Card",
  139. "props": { "title": "My Card" },
  140. "children": ["text-1"]
  141. },
  142. "text-1": { ... }
  143. }
  144. }
  145. ```
  146. ### Element Structure
  147. Each element in the map has a consistent shape:
  148. ```json
  149. {
  150. "type": "ComponentName",
  151. "props": { "label": "Hello" },
  152. "children": ["child-1", "child-2"]
  153. }
  154. ```
  155. - `type` — Component type from your catalog
  156. - `props` — Component properties
  157. - `children` — Array of child element keys
  158. ### Dynamic Data
  159. Props can reference data from the state model using `$state` expressions. The value is a JSON Pointer (RFC 6901) path into the state:
  160. ```json
  161. {
  162. "type": "Metric",
  163. "props": {
  164. "label": "Total Revenue",
  165. "value": { "$state": "/metrics/revenue" },
  166. "change": { "$state": "/metrics/revenueChange" }
  167. },
  168. "children": []
  169. }
  170. ```
  171. See [Data Binding](/docs/data-binding) for the full reference including `$item`, `$index`, repeat, and two-way binding.
  172. ### Conditional Visibility
  173. Control when elements appear using the `visible` property:
  174. ```json
  175. {
  176. "type": "Alert",
  177. "props": {
  178. "message": "You have unsaved changes"
  179. },
  180. "children": [],
  181. "visible": {
  182. "$state": "/form/isDirty",
  183. "eq": true
  184. }
  185. }
  186. ```
  187. ## Working with Specs
  188. ### Validating a Spec
  189. Use `validateSpec` from `@json-render/core` to check a spec for structural issues:
  190. ```typescript
  191. import { validateSpec } from '@json-render/core';
  192. const result = validateSpec(spec);
  193. if (!result.valid) {
  194. console.error('Invalid spec:', result.issues);
  195. }
  196. ```
  197. ### Rendering a Spec (React)
  198. With `@json-render/react`, wrap the `Renderer` in providers to supply state and visibility:
  199. ```tsx
  200. import { Renderer, StateProvider, VisibilityProvider } from '@json-render/react';
  201. import { registry } from './registry';
  202. function MyApp({ spec, initialState }) {
  203. return (
  204. <StateProvider initialState={initialState}>
  205. <VisibilityProvider>
  206. <Renderer spec={spec} registry={registry} />
  207. </VisibilityProvider>
  208. </StateProvider>
  209. );
  210. }
  211. ```
  212. See the [@json-render/react API reference](/docs/api/react) for full provider and hook documentation.
  213. ### Streaming a Spec (React)
  214. With `@json-render/react`, use the `useUIStream` hook to stream specs incrementally:
  215. ```tsx
  216. import { useUIStream } from '@json-render/react';
  217. function GenerativeUI() {
  218. const { spec, isStreaming } = useUIStream({
  219. api: '/api/generate',
  220. });
  221. return (
  222. <Renderer
  223. spec={spec}
  224. registry={registry}
  225. loading={isStreaming}
  226. />
  227. );
  228. }
  229. ```
  230. See [Streaming](/docs/streaming) for the full SpecStream format and server-side setup.
  231. ## Spec Sources
  232. Specs can come from various sources:
  233. - **AI Generation** — LLMs generate specs based on prompts and catalog
  234. - **Database** — Store specs as JSON and load dynamically
  235. - **API Response** — Server returns specs based on user/context
  236. - **Static Files** — Pre-built specs for known UI patterns
  237. ## Next
  238. Learn about [catalogs](/docs/catalog) — the vocabulary of components available in your specs.