| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- import Link from "next/link";
- import { Code } from "@/components/code";
- export const metadata = {
- title: "Specs | json-render",
- };
- export default function SpecsPage() {
- return (
- <article>
- <h1 className="text-3xl font-bold mb-4">Specs</h1>
- <p className="text-muted-foreground mb-8">
- A spec is a JSON document that describes your UI.
- </p>
- <h2 className="text-xl font-semibold mt-12 mb-4">What is a Spec?</h2>
- <p className="text-sm text-muted-foreground mb-4">
- A spec (specification) is the actual JSON that describes a UI. It
- conforms to a{" "}
- <Link href="/docs/schemas" className="text-foreground hover:underline">
- schema
- </Link>{" "}
- and uses components from a{" "}
- <Link href="/docs/catalog" className="text-foreground hover:underline">
- catalog
- </Link>
- . Specs can be:
- </p>
- <ul className="list-disc list-inside text-sm text-muted-foreground space-y-1 mb-4">
- <li>Generated by AI in real-time</li>
- <li>Stored in a database</li>
- <li>Streamed progressively from a server</li>
- <li>Hand-authored as JSON files</li>
- </ul>
- <p className="text-sm text-muted-foreground mb-4">
- json-render is schema-agnostic — your specs can follow any JSON
- structure you choose.
- </p>
- <h2 className="text-xl font-semibold mt-12 mb-4">Example Specs</h2>
- <h3 className="text-lg font-medium mt-8 mb-3">Simple Spec</h3>
- <p className="text-sm text-muted-foreground mb-4">
- A basic spec using the{" "}
- <code className="text-foreground">@json-render/react</code> schema. Note
- the flat structure with a <code className="text-foreground">root</code>{" "}
- key and <code className="text-foreground">elements</code> map:
- </p>
- <Code lang="json">{`{
- "root": "card-1",
- "elements": {
- "card-1": {
- "key": "card-1",
- "type": "Card",
- "props": { "title": "Welcome" },
- "children": ["text-1"],
- "parentKey": ""
- },
- "text-1": {
- "key": "text-1",
- "type": "Text",
- "props": { "content": "Hello, $data.user.name!" },
- "children": [],
- "parentKey": "card-1"
- }
- }
- }`}</Code>
- <h3 className="text-lg font-medium mt-8 mb-3">Complex Spec</h3>
- <p className="text-sm text-muted-foreground mb-4">
- A more complex spec with multiple nested elements:
- </p>
- <Code lang="json">{`{
- "root": "card-1",
- "elements": {
- "card-1": {
- "key": "card-1",
- "type": "Card",
- "props": { "title": "User Profile", "padding": "md" },
- "children": ["row-1", "button-1"],
- "parentKey": ""
- },
- "row-1": {
- "key": "row-1",
- "type": "Row",
- "props": { "gap": "md" },
- "children": ["avatar-1", "stack-1"],
- "parentKey": "card-1"
- },
- "avatar-1": {
- "key": "avatar-1",
- "type": "Avatar",
- "props": { "src": "$data.user.avatar", "alt": "$data.user.name" },
- "children": [],
- "parentKey": "row-1"
- },
- "stack-1": {
- "key": "stack-1",
- "type": "Stack",
- "props": { "gap": "sm" },
- "children": ["name-text", "email-text"],
- "parentKey": "row-1"
- },
- "name-text": {
- "key": "name-text",
- "type": "Text",
- "props": { "content": "$data.user.name", "variant": "heading" },
- "children": [],
- "parentKey": "stack-1"
- },
- "email-text": {
- "key": "email-text",
- "type": "Text",
- "props": { "content": "$data.user.email", "variant": "caption" },
- "children": [],
- "parentKey": "stack-1"
- },
- "button-1": {
- "key": "button-1",
- "type": "Button",
- "props": { "label": "Edit Profile" },
- "children": [],
- "parentKey": "card-1"
- }
- }
- }`}</Code>
- <h3 className="text-lg font-medium mt-8 mb-3">Block-Level Spec</h3>
- <p className="text-sm text-muted-foreground mb-4">
- A high-level spec using semantic blocks for page layouts:
- </p>
- <Code lang="json">{`{
- "root": "page",
- "elements": {
- "page": {
- "key": "page",
- "type": "Page",
- "props": {},
- "children": ["header", "hero", "features", "footer"],
- "parentKey": ""
- },
- "header": {
- "key": "header",
- "type": "Header",
- "props": { "logo": "/logo.svg", "navItems": ["Products", "Pricing", "Docs"] },
- "children": [],
- "parentKey": "page"
- },
- "hero": {
- "key": "hero",
- "type": "Hero",
- "props": {
- "title": "Build UIs with JSON",
- "subtitle": "Let AI generate your interfaces",
- "ctaLabel": "Get Started",
- "ctaHref": "/docs"
- },
- "children": [],
- "parentKey": "page"
- },
- "features": {
- "key": "features",
- "type": "Features",
- "props": { "columns": 3 },
- "children": ["feature-1", "feature-2", "feature-3"],
- "parentKey": "page"
- },
- "feature-1": {
- "key": "feature-1",
- "type": "Feature",
- "props": { "icon": "zap", "title": "Fast", "description": "Render UIs in milliseconds" },
- "children": [],
- "parentKey": "features"
- },
- "feature-2": {
- "key": "feature-2",
- "type": "Feature",
- "props": { "icon": "shield", "title": "Secure", "description": "Validate all specs against your catalog" },
- "children": [],
- "parentKey": "features"
- },
- "feature-3": {
- "key": "feature-3",
- "type": "Feature",
- "props": { "icon": "sparkles", "title": "AI-Ready", "description": "Generate prompts from your catalog" },
- "children": [],
- "parentKey": "features"
- },
- "footer": {
- "key": "footer",
- "type": "Footer",
- "props": { "copyright": "2025 Acme Inc", "links": ["Privacy", "Terms", "Contact"] },
- "children": [],
- "parentKey": "page"
- }
- }
- }`}</Code>
- <h2 className="text-xl font-semibold mt-12 mb-4">Spec Anatomy</h2>
- <h3 className="text-lg font-medium mt-8 mb-3">Root and Elements</h3>
- <p className="text-sm text-muted-foreground mb-4">
- Every spec has a <code className="text-foreground">root</code> key
- pointing to the entry element, and an{" "}
- <code className="text-foreground">elements</code> map containing all
- elements:
- </p>
- <Code lang="json">{`{
- "root": "card-1",
- "elements": {
- "card-1": {
- "key": "card-1",
- "type": "Card",
- "props": { "title": "My Card" },
- "children": ["text-1"],
- "parentKey": ""
- },
- "text-1": { ... }
- }
- }`}</Code>
- <h3 className="text-lg font-medium mt-8 mb-3">Element Structure</h3>
- <p className="text-sm text-muted-foreground mb-4">
- Each element in the map has a consistent shape:
- </p>
- <Code lang="json">{`{
- "key": "unique-id",
- "type": "ComponentName",
- "props": { "label": "Hello" },
- "children": ["child-1", "child-2"],
- "parentKey": "parent-id"
- }`}</Code>
- <ul className="list-disc list-inside text-sm text-muted-foreground space-y-1 mt-3 mb-4">
- <li>
- <code className="text-foreground">key</code> — Unique identifier for
- this element
- </li>
- <li>
- <code className="text-foreground">type</code> — Component type from
- your catalog
- </li>
- <li>
- <code className="text-foreground">props</code> — Component properties
- </li>
- <li>
- <code className="text-foreground">children</code> — Array of child
- element keys
- </li>
- <li>
- <code className="text-foreground">parentKey</code> — Key of parent
- element (empty string for root)
- </li>
- </ul>
- <h3 className="text-lg font-medium mt-8 mb-3">Dynamic Data</h3>
- <p className="text-sm text-muted-foreground mb-4">
- Props can reference data using{" "}
- <code className="text-foreground">$data</code> paths:
- </p>
- <Code lang="json">{`{
- "key": "metric-1",
- "type": "Metric",
- "props": {
- "label": "Total Revenue",
- "value": "$data.metrics.revenue",
- "change": "$data.metrics.revenueChange"
- },
- "children": [],
- "parentKey": "dashboard"
- }`}</Code>
- <h3 className="text-lg font-medium mt-8 mb-3">Conditional Visibility</h3>
- <p className="text-sm text-muted-foreground mb-4">
- Control when elements appear using the{" "}
- <code className="text-foreground">visible</code> property:
- </p>
- <Code lang="json">{`{
- "key": "alert-1",
- "type": "Alert",
- "props": {
- "message": "You have unsaved changes"
- },
- "children": [],
- "parentKey": "form",
- "visible": {
- "path": "$data.form.isDirty",
- "operator": "eq",
- "value": true
- }
- }`}</Code>
- <h2 className="text-xl font-semibold mt-12 mb-4">Working with Specs</h2>
- <h3 className="text-lg font-medium mt-8 mb-3">Rendering a Spec</h3>
- <Code lang="tsx">{`import { Renderer } from '@json-render/react';
- function MyApp({ spec, data }) {
- return (
- <Renderer
- spec={spec}
- data={data}
- registry={registry}
- />
- );
- }`}</Code>
- <h3 className="text-lg font-medium mt-8 mb-3">Validating a Spec</h3>
- <Code lang="typescript">{`import { validate } from '@json-render/core';
- const result = validate(spec, catalog);
- if (!result.valid) {
- console.error('Invalid spec:', result.errors);
- }`}</Code>
- <h3 className="text-lg font-medium mt-8 mb-3">Streaming Specs</h3>
- <p className="text-sm text-muted-foreground mb-4">
- Specs can be streamed incrementally for progressive rendering:
- </p>
- <Code lang="tsx">{`import { useUIStream } from '@json-render/react';
- function GenerativeUI() {
- const { spec, isStreaming } = useUIStream({
- api: '/api/generate',
- });
- return (
- <Renderer
- spec={spec}
- registry={registry}
- loading={isStreaming}
- />
- );
- }`}</Code>
- <h2 className="text-xl font-semibold mt-12 mb-4">Spec Sources</h2>
- <p className="text-sm text-muted-foreground mb-4">
- Specs can come from various sources:
- </p>
- <ul className="list-disc list-inside text-sm text-muted-foreground space-y-1 mb-4">
- <li>
- <strong className="text-foreground">AI Generation</strong> — LLMs
- generate specs based on prompts and catalog
- </li>
- <li>
- <strong className="text-foreground">Database</strong> — Store specs as
- JSON and load dynamically
- </li>
- <li>
- <strong className="text-foreground">API Response</strong> — Server
- returns specs based on user/context
- </li>
- <li>
- <strong className="text-foreground">Static Files</strong> — Pre-built
- specs for known UI patterns
- </li>
- </ul>
- <h2 className="text-xl font-semibold mt-12 mb-4">Next</h2>
- <p className="text-sm text-muted-foreground">
- Learn about{" "}
- <Link href="/docs/catalog" className="text-foreground hover:underline">
- catalogs
- </Link>{" "}
- — the vocabulary of components available in your specs.
- </p>
- </article>
- );
- }
|