Jsonablejsonable

Code Export

Export generated UI as standalone code for your framework.

Overview

While Jsonable 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 Jsonable 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 {
  traverseTree,          // Walk the UI tree
  collectUsedComponents, // Get all component types used
  collectDataPaths,      // 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(tree: UITree): GeneratedFile[] {
  const components = collectUsedComponents(tree);
  
  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 scripts
  • tsconfig.json - TypeScript config
  • next.config.js - Next.js config
  • app/layout.tsx - Root layout
  • app/globals.css - Global styles
  • app/page.tsx - Generated page with data
  • components/ui/*.tsx - Standalone components

Standalone Components

The exported components are standalone with no Jsonable dependencies. They receive data as props instead of using hooks:

// Generated component (standalone)
interface MetricProps {
  label: string;
  valuePath: string;
  data?: Record<string, unknown>;
}

export function Metric({ label, valuePath, data }: MetricProps) {
  const value = data ? getByPath(data, valuePath) : undefined;
  return (
    <div>
      <span>{label}</span>
      <span>{formatValue(value)}</span>
    </div>
  );
}

Using the Utilities

traverseTree

import { traverseTree } from '@json-render/codegen';

traverseTree(tree, (element, depth, parent) => {
  console.log(' '.repeat(depth * 2) + element.type);
});

collectUsedComponents

import { collectUsedComponents } from '@json-render/codegen';

const components = collectUsedComponents(tree);
// 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"

Ready to get started?

Join hundreds of teams using jsonable to empower their users with AI-generated UIs.