page.mdx 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  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 conforms to a [schema](/docs/schemas) and uses components from a [catalog](/docs/catalog). 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": "Hello, $data.user.name!" },
  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": "$data.user.avatar", "alt": "$data.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": "$data.user.name", "variant": "heading" },
  60. "children": []
  61. },
  62. "email-text": {
  63. "type": "Text",
  64. "props": { "content": "$data.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. ### Root and Elements
  131. Every spec has a `root` key pointing to the entry element, and an `elements` map containing all elements:
  132. ```json
  133. {
  134. "root": "card-1",
  135. "elements": {
  136. "card-1": {
  137. "type": "Card",
  138. "props": { "title": "My Card" },
  139. "children": ["text-1"]
  140. },
  141. "text-1": { ... }
  142. }
  143. }
  144. ```
  145. ### Element Structure
  146. Each element in the map has a consistent shape:
  147. ```json
  148. {
  149. "type": "ComponentName",
  150. "props": { "label": "Hello" },
  151. "children": ["child-1", "child-2"]
  152. }
  153. ```
  154. - `type` — Component type from your catalog
  155. - `props` — Component properties
  156. - `children` — Array of child element keys
  157. ### Dynamic Data
  158. Props can reference data using `$data` paths:
  159. ```json
  160. {
  161. "type": "Metric",
  162. "props": {
  163. "label": "Total Revenue",
  164. "value": "$data.metrics.revenue",
  165. "change": "$data.metrics.revenueChange"
  166. },
  167. "children": []
  168. }
  169. ```
  170. ### Conditional Visibility
  171. Control when elements appear using the `visible` property:
  172. ```json
  173. {
  174. "type": "Alert",
  175. "props": {
  176. "message": "You have unsaved changes"
  177. },
  178. "children": [],
  179. "visible": {
  180. "path": "$data.form.isDirty",
  181. "operator": "eq",
  182. "value": true
  183. }
  184. }
  185. ```
  186. ## Working with Specs
  187. ### Rendering a Spec
  188. ```tsx
  189. import { Renderer } from '@json-render/react';
  190. function MyApp({ spec, data }) {
  191. return (
  192. <Renderer
  193. spec={spec}
  194. data={data}
  195. registry={registry}
  196. />
  197. );
  198. }
  199. ```
  200. ### Validating a Spec
  201. ```typescript
  202. import { validate } from '@json-render/core';
  203. const result = validate(spec, catalog);
  204. if (!result.valid) {
  205. console.error('Invalid spec:', result.errors);
  206. }
  207. ```
  208. ### Streaming Specs
  209. Specs can be streamed incrementally for progressive rendering:
  210. ```tsx
  211. import { useUIStream } from '@json-render/react';
  212. function GenerativeUI() {
  213. const { spec, isStreaming } = useUIStream({
  214. api: '/api/generate',
  215. });
  216. return (
  217. <Renderer
  218. spec={spec}
  219. registry={registry}
  220. loading={isStreaming}
  221. />
  222. );
  223. }
  224. ```
  225. ## Spec Sources
  226. Specs can come from various sources:
  227. - **AI Generation** — LLMs generate specs based on prompts and catalog
  228. - **Database** — Store specs as JSON and load dynamically
  229. - **API Response** — Server returns specs based on user/context
  230. - **Static Files** — Pre-built specs for known UI patterns
  231. ## Next
  232. Learn about [catalogs](/docs/catalog) — the vocabulary of components available in your specs.