page.mdx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import { pageMetadata } from "@/lib/page-metadata"
  2. export const metadata = pageMetadata("docs/directives")
  3. # Directives
  4. Extend the spec language with custom `$`-prefixed dynamic values. Directives let you add formatting, math, string manipulation, i18n, and any other transformation without modifying core.
  5. ## Overview
  6. A directive is a user-defined dynamic value expression, like `$state` or `$computed`, but defined in userland. Each directive has a `$`-prefixed name, a Zod schema for validation, and a resolver function.
  7. ```json
  8. {
  9. "type": "Text",
  10. "props": {
  11. "text": {
  12. "$format": "currency",
  13. "value": { "$state": "/cart/total" },
  14. "currency": "USD"
  15. }
  16. },
  17. "children": []
  18. }
  19. ```
  20. ## Defining a Directive
  21. Use `defineDirective` from `@json-render/core`:
  22. ```typescript
  23. import { defineDirective, resolvePropValue } from '@json-render/core';
  24. import { z } from 'zod';
  25. const doubleDirective = defineDirective({
  26. name: '$double',
  27. description: 'Double a numeric value.',
  28. schema: z.object({
  29. $double: z.unknown(),
  30. }),
  31. resolve(value, ctx) {
  32. const resolved = resolvePropValue(value.$double, ctx);
  33. return (resolved as number) * 2;
  34. },
  35. });
  36. ```
  37. The `description` field is optional. When generating prompts, the directive's schema fields are auto-described from the Zod schema; the `description` adds short behavioral context the schema can't express.
  38. ## Wiring Directives
  39. Pass directives to both the renderer (for runtime resolution) and the catalog prompt (for AI generation).
  40. ### Runtime
  41. ```tsx
  42. import { JSONUIProvider, Renderer } from '@json-render/react';
  43. import { standardDirectives } from '@json-render/directives';
  44. <JSONUIProvider registry={registry} directives={standardDirectives}>
  45. <Renderer spec={spec} registry={registry} />
  46. </JSONUIProvider>
  47. ```
  48. Or with `createRenderer`:
  49. ```tsx
  50. const MyRenderer = createRenderer(catalog, components);
  51. <MyRenderer spec={spec} directives={directives} />
  52. ```
  53. All four renderers (React, Vue, Svelte, Solid) accept the `directives` prop on their provider and `createRenderer` output.
  54. ### Prompt Generation
  55. ```typescript
  56. const prompt = catalog.prompt({ directives });
  57. ```
  58. Each directive's schema is auto-described in the "CUSTOM DYNAMIC VALUES" section of the system prompt. The optional `description` field adds behavioral context inline.
  59. ## Pre-built Directives
  60. The `@json-render/directives` package ships ready-to-use directives:
  61. ```typescript
  62. import { standardDirectives, createI18nDirective } from '@json-render/directives';
  63. ```
  64. `standardDirectives` includes `$format`, `$math`, `$concat`, `$count`, `$truncate`, `$pluralize`, and `$join`. Add factory directives by spreading:
  65. ```typescript
  66. const directives = [...standardDirectives, createI18nDirective(config)];
  67. ```
  68. See the [API reference](/docs/api/directives) for details on each directive.
  69. ## Composition
  70. Directives compose naturally. Each resolver calls `resolvePropValue` on its inputs, so directives can wrap other directives or built-in expressions like `$state`:
  71. ```json
  72. {
  73. "$format": "currency",
  74. "value": {
  75. "$math": "multiply",
  76. "a": { "$state": "/price" },
  77. "b": { "$state": "/qty" }
  78. },
  79. "currency": "USD"
  80. }
  81. ```
  82. This resolves inside-out: `$state` reads from state, `$math` multiplies the values, and `$format` formats the result as currency.
  83. ## Built-in Precedence
  84. Built-in expressions (`$state`, `$computed`, `$cond`, `$template`, etc.) always take precedence over custom directives. `defineDirective` throws if you try to register a name that conflicts with a built-in key.
  85. ## Next
  86. - [API Reference](/docs/api/directives) — full directive reference
  87. - [Computed Values](/docs/computed-values) — `$computed` and `$template` expressions
  88. - [Data Binding](/docs/data-binding) — `$state`, `$item`, and binding expressions