Code Export
Export generated UI as standalone code for your framework.
Overview
While json-render is designed for dynamic rendering, you can export generated UI as static code. The code generation is intentionally project-specific so you have full control over:
- Component templates (standalone, no json-render dependencies)
- Package.json and project structure
- Framework-specific patterns (Next.js, Remix, etc.)
- How data is passed to components
Architecture
Code export is split into two parts:
1. @json-render/codegen (utilities)
Framework-agnostic utilities for building code generators:
import {
traverseSpec, // Walk the UI spec
collectUsedComponents, // Get all component types used
collectStatePaths, // Get all data binding paths
collectActions, // Get all action names
serializeProps, // Convert props to JSX string
} from '@json-render/codegen';2. Your Project (generator)
Custom code generator specific to your project and framework:
// lib/codegen/generator.ts
import { collectUsedComponents, serializeProps } from '@json-render/codegen';
export function generateNextJSProject(spec: Spec): GeneratedFile[] {
const components = collectUsedComponents(spec);
return [
{ path: 'package.json', content: '...' },
{ path: 'app/page.tsx', content: '...' },
// ... component files
];
}Example: Next.js Export
See the dashboard example for a complete implementation that exports:
package.json- Dependencies and scriptstsconfig.json- TypeScript confignext.config.js- Next.js configapp/layout.tsx- Root layoutapp/globals.css- Global stylesapp/page.tsx- Generated page with datacomponents/ui/*.tsx- Standalone components
Standalone Components
The exported components are standalone with no json-render dependencies. They receive data as props instead of using hooks:
// Generated component (standalone)
interface MetricProps {
label: string;
statePath: string;
data?: Record<string, unknown>;
}
export function Metric({ label, statePath, data }: MetricProps) {
const value = data ? getByPath(data, statePath) : undefined;
return (
<div>
<span>{label}</span>
<span>{formatValue(value)}</span>
</div>
);
}Using the Utilities
traverseSpec
import { traverseSpec } from '@json-render/codegen';
traverseSpec(spec, (element, key, depth, parent) => {
console.log(' '.repeat(depth * 2) + `${key}: ${element.type}`);
});collectUsedComponents
import { collectUsedComponents } from '@json-render/codegen';
const components = collectUsedComponents(spec);
// Set { 'Card', 'Metric', 'Chart', 'Table' }
// Generate only the needed component files
for (const component of components) {
files.push({
path: `components/ui/${component.toLowerCase()}.tsx`,
content: componentTemplates[component],
});
}serializeProps
import { serializeProps } from '@json-render/codegen';
const propsStr = serializeProps({
title: 'Dashboard',
columns: 3,
disabled: true,
});
// 'title="Dashboard" columns={3} disabled'Try It
Run the dashboard example and click "Export Project" to see code generation in action:
cd examples/dashboard
pnpm dev
# Open http://localhost:3001
# Generate a widget, then click "Export Project"