page.mdx 6.9 KB

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