Rendering Pages
Render UI Builder pages in production using the LayerRenderer
component. Display your designed pages without the editor interface, with full support for dynamic content through variables.
Basic Rendering Demo
Variables Demo
Basic Usage
Use the LayerRenderer
component to display UI Builder pages without the editor interface:
tsx1import LayerRenderer from '@/components/ui/ui-builder/layer-renderer'; 2import { ComponentLayer, ComponentRegistry } from '@/components/ui/ui-builder/types'; 3 4// Your component registry (same as used in UIBuilder) 5const myComponentRegistry: ComponentRegistry = { 6 // Your component definitions... 7}; 8 9// Page data from UIBuilder or database 10const page: ComponentLayer = { 11 id: "welcome-page", 12 type: "div", 13 name: "Welcome Page", 14 props: { 15 className: "p-6 max-w-4xl mx-auto" 16 }, 17 children: [ 18 { 19 id: "title", 20 type: "h1", 21 name: "Title", 22 props: { 23 className: "text-3xl font-bold mb-4" 24 }, 25 children: "Welcome to My App" 26 }, 27 { 28 id: "description", 29 type: "p", 30 name: "Description", 31 props: { 32 className: "text-gray-600" 33 }, 34 children: "This page was built with UI Builder." 35 } 36 ] 37}; 38 39function MyRenderedPage() { 40 return ( 41 <LayerRenderer 42 page={page} 43 componentRegistry={myComponentRegistry} 44 /> 45 ); 46}
Rendering with Variables
Make your pages dynamic by binding component properties to variables:
tsx1import LayerRenderer from '@/components/ui/ui-builder/layer-renderer'; 2import { Variable } from '@/components/ui/ui-builder/types'; 3 4// Define your variables 5const variables: Variable[] = [ 6 { 7 id: "userName", 8 name: "User Name", 9 type: "string", 10 defaultValue: "Guest" 11 }, 12 { 13 id: "userAge", 14 name: "User Age", 15 type: "number", 16 defaultValue: 25 17 }, 18 { 19 id: "showWelcomeMessage", 20 name: "Show Welcome Message", 21 type: "boolean", 22 defaultValue: true 23 } 24]; 25 26// Page with variable bindings (created in UIBuilder) 27const pageWithVariables: ComponentLayer = { 28 id: "user-profile", 29 type: "div", 30 props: { 31 className: "p-6 bg-white rounded-lg shadow" 32 }, 33 children: [ 34 { 35 id: "welcome-message", 36 type: "h2", 37 props: { 38 className: "text-2xl font-bold mb-2", 39 children: { __variableRef: "userName" } // Bound to userName variable 40 }, 41 children: [] 42 }, 43 { 44 id: "age-display", 45 type: "p", 46 props: { 47 className: "text-gray-600", 48 children: { __variableRef: "userAge" } // Bound to userAge variable 49 }, 50 children: [] 51 } 52 ] 53}; 54 55// Provide runtime values for variables 56const variableValues = { 57 userName: "Jane Smith", 58 userAge: 28, 59 showWelcomeMessage: true 60}; 61 62function DynamicUserProfile() { 63 return ( 64 <LayerRenderer 65 page={pageWithVariables} 66 componentRegistry={myComponentRegistry} 67 variables={variables} 68 variableValues={variableValues} 69 /> 70 ); 71}
Production Integration
Integrate with your data sources to create personalized experiences:
tsx1function CustomerPage({ customerId }: { customerId: string }) { 2 const [pageData, setPageData] = useState<ComponentLayer | null>(null); 3 const [customerData, setCustomerData] = useState({}); 4 5 useEffect(() => { 6 async function loadData() { 7 // Load page structure from your CMS/database 8 const pageResponse = await fetch('/api/pages/customer-dashboard'); 9 const page = await pageResponse.json(); 10 11 // Load customer-specific data 12 const customerResponse = await fetch(`/api/customers/${customerId}`); 13 const customer = await customerResponse.json(); 14 15 setPageData(page); 16 setCustomerData(customer); 17 } 18 19 loadData(); 20 }, [customerId]); 21 22 if (!pageData) return <div>Loading...</div>; 23 24 return ( 25 <LayerRenderer 26 page={pageData} 27 componentRegistry={myComponentRegistry} 28 variables={variables} 29 variableValues={{ 30 customerName: customerData.name, 31 companyLogo: customerData.logoUrl, 32 brandColor: customerData.primaryColor, 33 // Inject real customer data into the template 34 }} 35 /> 36 ); 37}
Performance Optimization
Optimize rendering performance for production:
tsx1// Memoize the renderer to prevent unnecessary re-renders 2const MemoizedRenderer = React.memo(LayerRenderer, (prevProps, nextProps) => { 3 return ( 4 prevProps.page === nextProps.page && 5 JSON.stringify(prevProps.variableValues) === JSON.stringify(nextProps.variableValues) 6 ); 7}); 8 9// Use in your component 10function OptimizedPage() { 11 return ( 12 <MemoizedRenderer 13 page={page} 14 componentRegistry={myComponentRegistry} 15 variables={variables} 16 variableValues={variableValues} 17 /> 18 ); 19}
Error Handling
Handle rendering errors gracefully in production:
tsx1import { ErrorBoundary } from 'react-error-boundary'; 2 3function ErrorFallback({ error }: { error: Error }) { 4 return ( 5 <div className="p-4 bg-red-50 border border-red-200 rounded"> 6 <h2 className="text-lg font-semibold text-red-800">Page failed to load</h2> 7 <p className="text-red-600">{error.message}</p> 8 <button 9 onClick={() => window.location.reload()} 10 className="mt-2 px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700" 11 > 12 Reload Page 13 </button> 14 </div> 15 ); 16} 17 18function SafeRenderedPage() { 19 return ( 20 <ErrorBoundary FallbackComponent={ErrorFallback}> 21 <LayerRenderer 22 page={page} 23 componentRegistry={myComponentRegistry} 24 variables={variables} 25 variableValues={variableValues} 26 /> 27 </ErrorBoundary> 28 ); 29}
LayerRenderer Props
page
(required): TheComponentLayer
to rendercomponentRegistry
(required): Registry mapping component types to their definitionsclassName
: Optional CSS class for the root containervariables
: Array ofVariable
definitions available for bindingvariableValues
: Object mapping variable IDs to runtime values (overrides defaults)editorConfig
: Internal editor configuration (rarely needed in production)
Best Practices
- Use the same
componentRegistry
in bothUIBuilder
andLayerRenderer
- Validate variable values before passing to LayerRenderer to prevent runtime errors
- Handle loading states while fetching page data and variables
- Implement error boundaries to gracefully handle rendering failures
- Cache page data when possible for better performance
- Memoize expensive variable calculations to avoid unnecessary re-computations
- Test variable bindings thoroughly to ensure robustness across different data scenarios