@superatomai/renderer-react
v0.0.2
Published
SuperAtom Runtime SDK - React components for rendering JSON UI schemas
Downloads
73
Maintainers
Readme
@superatomai/renderer-react
React components for rendering JSON UI schemas from SuperAtom responses.
Table of Contents
- Installation
- Quick Start
- API Reference
- Available Components
- Dynamic Component Loading from Bundles
- JSON Schema Structure
- Advanced Usage
- TypeScript Support
- Development
Installation
pnpm add @superatomai/renderer-react
# or
npm install @superatomai/renderer-react
# or
yarn add @superatomai/renderer-reactPeer Dependencies
This package requires React 18+:
pnpm add react react-domQuick Start
1. Basic Usage
import { useState } from 'react';
import { DSLRenderer } from '@superatomai/renderer-react';
function DashboardPanel() {
const [uiSchema, setUiSchema] = useState(null);
const [uiData, setUiData] = useState(null);
const handleRenderUI = (schema: any, data: any) => {
setUiSchema(schema);
setUiData(data || {});
};
return (
<div>
{uiSchema && (
<DSLRenderer
dsl={uiSchema}
data={uiData}
handlers={{}}
/>
)}
</div>
);
}
export default DashboardPanel;2. Report Generation
import { useState } from 'react';
import { DSLRenderer } from '@superatomai/renderer-react';
function ReportGenerator() {
const [reports, setReports] = useState([]);
const addReport = (schema: any, data: any) => {
setReports(prev => [...prev, {
id: Date.now(),
schema,
data: data || {}
}]);
};
return (
<div className="report-container">
{reports.map(report => (
<div key={report.id} className="report-card">
<DSLRenderer
dsl={report.schema}
data={report.data}
/>
</div>
))}
</div>
);
}3. Dashboard with Multiple Panels
import { useState } from 'react';
import { DSLRenderer } from '@superatomai/renderer-react';
interface DashboardPanel {
id: string;
title: string;
schema: any;
data: any;
}
function Dashboard() {
const [panels, setPanels] = useState<DashboardPanel[]>([]);
const addPanel = (title: string, schema: any, data: any) => {
setPanels(prev => [...prev, {
id: `panel-${Date.now()}`,
title,
schema,
data
}]);
};
return (
<div className="grid grid-cols-2 gap-4">
{panels.map(panel => (
<div key={panel.id} className="panel-card">
<h3>{panel.title}</h3>
<DSLRenderer
dsl={panel.schema}
data={panel.data}
/>
</div>
))}
</div>
);
}API Reference
<DSLRenderer>
Main component for rendering JSON UI schemas.
Props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| dsl | UIComponent | Yes | The UI schema to render |
| data | Record<string, any> | No | Data context for the UI |
| context | Record<string, any> | No | Additional context variables |
| handlers | Record<string, Function> | No | Event handlers for components |
| onNavigate | (uiid: string, params?: Record<string, any>) => void | No | Navigation handler |
Component Registry
COMP_REGISTRY
Global registry containing all available components (both native and dynamically loaded).
import { COMP_REGISTRY } from '@superatomai/renderer-react';
// Access the registry
console.log(Object.keys(COMP_REGISTRY)); // ['COMP_ECHART', 'COMP_AGGRID', ...]getComponentStates()
Returns the current state of all components.
import { getComponentStates } from '@superatomai/renderer-react';
const states = getComponentStates();
// Returns: { loaded: string[], loading: string[], failed: string[] }resetFailedComponent(componentId: string)
Resets the failed state of a specific component, allowing it to be retried.
import { resetFailedComponent } from '@superatomai/renderer-react';
resetFailedComponent('echarts'); // Allows echarts to be loaded againresetAllFailedComponents()
Resets all failed components.
import { resetAllFailedComponents } from '@superatomai/renderer-react';
const count = resetAllFailedComponents(); // Returns number of components reset<DynamicComponent>
Low-level component for rendering registry components directly (rarely needed).
import { DynamicComponent } from '@superatomai/renderer-react';
<DynamicComponent
type="COMP_ECHART"
props={{ option: {...} }}
/>Example
<DSLRenderer
dsl={{
id: 'chart-component',
name: 'ChartComponent',
data: { chartData: [...] },
render: {
id: 'render-root',
type: 'COMP_ECHART',
props: {
option: { $bind: 'chartData' }
}
}
}}
data={{ chartData: [...] }}
handlers={{
onChartClick: (params) => console.log('Chart clicked', params)
}}
/>Available Components
The SDK includes the following native components:
- COMP_ECHART - ECharts for data visualization
- COMP_AGGRID - AG Grid for data tables
- COMP_HANDSONTABLE - Handsontable for spreadsheets
- COMP_LEAFLET - Leaflet for maps
- COMP_MAPBOX - Mapbox GL for advanced maps
- COMP_VIS_NETWORK - Network/graph visualizations
- COMP_THREE_SCENE - 3D visualizations with Three.js
- COMP_PDF_VIEWER - PDF document viewer
- COMP_LUCKYSHEET - Excel-like spreadsheet
- COMP_MARKDOWN - Markdown renderer
- COMP_ICONIFY_ICON - Icon library
Dynamic Component Loading from Bundles
This package supports automatic registration of custom components from external bundles. This allows you to build your own components separately, bundle them, and have them automatically loaded into the component registry at runtime.
How It Works
Package Initialization: When you import
@superatomai/renderer-react, it automatically:- Sets up an event listener for component loading
- Exposes
window.runtime.componentsLoaded()for bundles to call - Watches
window.SUPERATOM.COMPONENTSfor new components
Bundle Setup: Your bundle calls
setup()from@superatomai/sdk-weband triggers the eventAuto-Registration: Components are wrapped and registered in
COMP_REGISTRYwith keys likeCOMP_YourComponent
Creating a Bundle with Custom Components
Step 1: Define Your Components
// src/components/DynamicBarChart.tsx
export default function DynamicBarChart({ data, config }) {
return (
<div>
{/* Your custom bar chart implementation */}
</div>
);
}Step 2: Setup Bundle Entry Point
// src/superatom.ts
import { createRoot } from 'react-dom/client';
import { createElement } from 'react';
import { setup } from '@superatomai/sdk-web';
import DynamicBarChart from './components/DynamicBarChart';
import DynamicDataTable from './components/DynamicDataTable';
const COMPONENTS = {
DynamicBarChart,
DynamicDataTable,
// Add your other components...
// REQUIRED: mount function for React version isolation
mount: (Component, container, props) => {
// Use the bundle's own React instance to create a root
const root = createRoot(container);
root.render(createElement(Component, props));
return { unmount: () => root.unmount() };
}
};
const SUPERATOM = {
init: async () => {
// Register components with setup()
setup(COMPONENTS);
// Trigger component registration event
(window as any).runtime?.componentsLoaded();
// Send metadata to SDK if needed
const sdkClient = (window as any).SA_WEB_SDK_CLIENT;
if (sdkClient) {
sdkClient.sendComponents(componentsMetadata);
}
},
destroy: () => {
// Cleanup if needed
}
};
export default SUPERATOM;Important: The
mountfunction is required for React version isolation. It ensures your bundle's React instance is used to render components, preventing version conflicts with the host application.
Step 3: Build Your Bundle
Use your preferred bundler (Vite, Webpack, etc.) to create a UMD or IIFE bundle that exposes SUPERATOM globally.
Using Bundles in Your Application
Option 1: Load Bundle Script Dynamically
import { useEffect, useState } from 'react';
import { DSLRenderer } from '@superatomai/renderer-react';
function App() {
const [bundleLoaded, setBundleLoaded] = useState(false);
useEffect(() => {
// Load bundle script
const script = document.createElement('script');
script.src = 'https://your-cdn.com/bundle.js';
script.onload = () => {
// Initialize bundle after package is ready
if (window.SUPERATOM?.init) {
window.SUPERATOM.init().then(() => {
setBundleLoaded(true);
});
}
};
document.head.appendChild(script);
}, []);
return (
<div>
{bundleLoaded ? (
<DSLRenderer
dsl={{
id: 'custom-chart',
render: {
id: 'chart-root',
type: 'COMP_DynamicBarChart', // Your custom component
props: {
data: { $bind: 'chartData' }
}
}
}}
data={{ chartData: [...] }}
/>
) : (
<div>Loading components...</div>
)}
</div>
);
}Option 2: Static Script Tag
<!DOCTYPE html>
<html>
<head>
<!-- Load your bundle -->
<script src="/path/to/bundle.js"></script>
</head>
<body>
<div id="root"></div>
<script type="module">
import { createRoot } from 'react-dom/client';
import App from './App';
// Initialize bundle components
window.SUPERATOM.init().then(() => {
createRoot(document.getElementById('root')).render(<App />);
});
</script>
</body>
</html>Important Considerations
⚠️ Loading Order: The npm package must be imported BEFORE calling window.SUPERATOM.init(), otherwise the event listener won't be set up yet.
✅ Best Practice Pattern:
// 1. Import package (sets up event listeners)
import { DSLRenderer } from '@superatomai/renderer-react';
// 2. Load bundle script
// 3. Call SUPERATOM.init() after both are ready
// 4. Render componentsAccessing Bundle Components in DSL
Once your bundle is loaded and initialized, components are automatically registered with the COMP_ prefix:
{
"id": "my-ui",
"render": {
"id": "root",
"type": "COMP_DynamicBarChart",
"props": {
"data": { "$bind": "salesData" },
"config": {
"title": "Sales by Region"
}
}
}
}Debugging Bundle Components
import { getComponentStates } from '@superatomai/renderer-react';
// Check component loading status
const states = getComponentStates();
console.log('Loaded components:', states.loaded);
console.log('Loading components:', states.loading);
console.log('Failed components:', states.failed);
// Access the registry directly (for debugging)
console.log('Available components:', window.COMP_REGISTRY);React Version Isolation
Bundle components are automatically wrapped with BundleComponentWrapper to ensure React version isolation. This means:
- Your bundle can use its own version of React
- The wrapper ensures proper rendering in the host application's React tree
- Each component has isolated error boundaries
JSON Schema Structure
UIComponent Schema
{
id: string; // Unique component ID
name?: string; // Component name
props?: Record<string, any>; // Component props
states?: Record<string, any>; // Component state
data?: Record<string, any>; // Component data
render: UIElement; // Root UI element to render
}UIElement Schema
{
id: string; // Unique element ID
type: string; // Element type (div, span, COMP_*)
props?: Record<string, any>; // Element props
children?: UIElement[] | string; // Child elements
// Conditional rendering
if?: { $exp: string };
else?: UIElement;
// Loop rendering
for?: {
in: { $bind: string } | { $exp: string };
as: string;
key?: string;
index?: string;
};
// Data binding
// Use { $bind: 'data.path' } for data binding
// Use { $exp: 'expression' } for expressions
}Example: Complete UI Schema
{
"id": "sales-dashboard",
"name": "SalesDashboard",
"data": {
"salesData": [
{ "region": "North", "sales": 1000 },
{ "region": "South", "sales": 1500 }
]
},
"render": {
"id": "root",
"type": "div",
"props": {
"style": { "padding": "20px" }
},
"children": [
{
"id": "title",
"type": "h1",
"children": "Sales Dashboard"
},
{
"id": "chart",
"type": "COMP_ECHART",
"props": {
"option": {
"xAxis": {
"type": "category",
"data": { "$exp": "salesData.map(d => d.region)" }
},
"yAxis": { "type": "value" },
"series": [{
"type": "bar",
"data": { "$exp": "salesData.map(d => d.sales)" }
}]
},
"style": { "height": "400px" }
}
}
]
}
}Advanced Usage
Custom Event Handlers
<DSLRenderer
dsl={schema}
data={data}
handlers={{
onRowClick: (row) => {
console.log('Row clicked:', row);
// Handle row click
},
onFilterChange: (filters) => {
console.log('Filters changed:', filters);
// Update filters
}
}}
/>Navigation Between UIs
<DSLRenderer
dsl={schema}
data={data}
onNavigate={(uiid, params) => {
console.log('Navigate to:', uiid, 'with params:', params);
// Load new UI based on uiid
loadUI(uiid, params);
}}
/>Error Handling
import { DSLErrorBoundary } from '@superatomai/renderer-react';
function App() {
return (
<DSLErrorBoundary>
<DSLRenderer dsl={schema} data={data} />
</DSLErrorBoundary>
);
}Component State Management
import { getComponentStates, resetFailedComponent } from '@superatomai/renderer-react';
// Check which components are loaded
const states = getComponentStates();
console.log('Loaded:', states.loaded);
console.log('Failed:', states.failed);
// Reset a failed component to retry loading
resetFailedComponent('echarts');TypeScript Support
Full TypeScript support with type definitions:
import type {
UIComponent,
UIElement,
DSLRendererProps
} from '@superatomai/renderer-react';
const schema: UIComponent = {
id: 'my-component',
render: {
id: 'root',
type: 'div',
children: 'Hello World'
}
};Development
# Install dependencies
pnpm install
# Build the package
pnpm build
# Watch mode for development
pnpm dev
# Type checking
pnpm typecheckLicense
MIT
Support
For issues and questions:
- GitHub Issues: superatom/sdk-runtime
- Documentation: docs.superatom.ai
