UI BuilderUI Builder
Core
  • Introduction
  • Quick Start
Component System
  • Components Intro
  • Shadcn Registry
  • Custom Components
  • Advanced Configuration
  • Panel Configuration
Data & Variables
  • Variables
  • Variable Binding
  • Function Registry
  • Editing Restrictions
Layout & Persistence
  • Layer Structure
  • State Management & Persistence
Rendering
  • Rendering Pages
  1. Layout & Persistence
  2. Layer Structure
Layer Structure

Understanding layer structure is fundamental to working with UI Builder. Layers define the hierarchical component tree that powers both the visual editor and the rendering system.

ComponentLayer Interface

Every element in UI Builder is represented as a ComponentLayer with this structure:

tsx
1interface ComponentLayer { 2 id: string; // Unique identifier 3 type: string; // Component type from registry 4 name?: string; // Optional display name for editor 5 props: Record<string, any>; // Component properties 6 children: ComponentLayer[] | string | VariableReference; // Child layers, text, or variable ref 7}

Core Fields

  • id: Required unique identifier for each layer
  • type: Must match a key in your component registry (e.g., 'Button', 'div', 'Card')
  • name: Optional display name shown in the layers panel
  • props: Object containing all component properties (className, variant, etc.)
  • children: Array of child layers, a string for text content, or a VariableReference for dynamic text
Loading...
Loading...
Loading...
Loading...
Loading...

Related Topics

  • Component Registry - Learn how to define which components are available
  • Variable Binding - Make your layers dynamic with variables
  • Rendering Pages - Render layers without the editor
  • Persistence - Save and load layer structures

Basic Layer Examples

Simple Text Layer

tsx
1const textLayer: ComponentLayer = { 2 id: 'heading-1', 3 type: 'h1', 4 name: 'Page Title', 5 props: { 6 className: 'text-3xl font-bold text-center' 7 }, 8 children: 'Welcome to My App' 9};

Button with Icon

tsx
1const buttonLayer: ComponentLayer = { 2 id: 'cta-button', 3 type: 'Button', 4 name: 'CTA Button', 5 props: { 6 variant: 'default', 7 size: 'lg', 8 className: 'w-full max-w-sm' 9 }, 10 children: [ 11 { 12 id: 'button-text', 13 type: 'span', 14 name: 'Button Text', 15 props: {}, 16 children: 'Get Started' 17 }, 18 { 19 id: 'button-icon', 20 type: 'Icon', 21 name: 'Arrow Icon', 22 props: { 23 iconName: 'ArrowRight', 24 size: 'medium' 25 }, 26 children: [] 27 } 28 ] 29};

Hierarchical Structure

Layers form a tree structure where containers hold other layers:

tsx
1const cardLayer: ComponentLayer = { 2 id: 'product-card', 3 type: 'div', 4 name: 'Product Card', 5 props: { 6 className: 'bg-white rounded-lg shadow-md p-6' 7 }, 8 children: [ 9 { 10 id: 'card-header', 11 type: 'div', 12 name: 'Header', 13 props: { className: 'mb-4' }, 14 children: [ 15 { 16 id: 'product-title', 17 type: 'h3', 18 name: 'Product Title', 19 props: { className: 'text-xl font-semibold' }, 20 children: 'Amazing Product' 21 }, 22 { 23 id: 'product-badge', 24 type: 'Badge', 25 name: 'Status Badge', 26 props: { variant: 'secondary' }, 27 children: 'New' 28 } 29 ] 30 }, 31 { 32 id: 'card-content', 33 type: 'div', 34 name: 'Content', 35 props: { className: 'space-y-3' }, 36 children: [ 37 { 38 id: 'description', 39 type: 'p', 40 name: 'Description', 41 props: { className: 'text-gray-600' }, 42 children: 'This product will change your life.' 43 }, 44 { 45 id: 'price', 46 type: 'div', 47 name: 'Price Container', 48 props: { className: 'flex items-center justify-between' }, 49 children: [ 50 { 51 id: 'price-text', 52 type: 'span', 53 name: 'Price', 54 props: { className: 'text-2xl font-bold text-green-600' }, 55 children: '$99.99' 56 }, 57 { 58 id: 'buy-button', 59 type: 'Button', 60 name: 'Buy Button', 61 props: { variant: 'default', size: 'sm' }, 62 children: 'Add to Cart' 63 } 64 ] 65 } 66 ] 67 } 68 ] 69};

Layer Types

Container Layers

Layers that hold and organize other layers:

tsx
1// Flex container 2{ 3 id: 'nav-container', 4 type: 'div', 5 name: 'Navigation', 6 props: { 7 className: 'flex items-center justify-between p-4' 8 }, 9 children: [/* nav items */] 10} 11 12// Grid container 13{ 14 id: 'grid-layout', 15 type: 'div', 16 name: 'Image Grid', 17 props: { 18 className: 'grid grid-cols-1 md:grid-cols-3 gap-6' 19 }, 20 children: [/* grid items */] 21}

Content Layers

Layers that display content:

tsx
1// Text content 2{ 3 id: 'paragraph-1', 4 type: 'p', 5 name: 'Description', 6 props: { 7 className: 'text-base leading-relaxed' 8 }, 9 children: 'Your content here...' 10} 11 12// Rich content 13{ 14 id: 'article-content', 15 type: 'Markdown', 16 name: 'Article Body', 17 props: {}, 18 children: '# Article Title\n\nThis is **markdown** content.' 19} 20 21// Images 22{ 23 id: 'hero-image', 24 type: 'img', 25 name: 'Hero Image', 26 props: { 27 src: '/hero.jpg', 28 alt: 'Hero image', 29 className: 'w-full h-64 object-cover' 30 }, 31 children: [] 32}

Interactive Layers

Layers that users can interact with:

tsx
1// Buttons 2{ 3 id: 'submit-btn', 4 type: 'Button', 5 name: 'Submit Button', 6 props: { 7 type: 'submit', 8 variant: 'default' 9 }, 10 children: 'Submit Form' 11} 12 13// Form inputs 14{ 15 id: 'email-input', 16 type: 'Input', 17 name: 'Email Field', 18 props: { 19 type: 'email', 20 placeholder: 'Enter your email', 21 className: 'w-full' 22 }, 23 children: [] 24}

Children Patterns

Layers can have different types of children:

Text Children

For simple text content:

tsx
1{ 2 id: 'simple-text', 3 type: 'p', 4 name: 'Paragraph', 5 props: {}, 6 children: 'This is simple text content' // string 7}

Component Children

For nested components:

tsx
1{ 2 id: 'container', 3 type: 'div', 4 name: 'Container', 5 props: {}, 6 children: [ // array of ComponentLayer objects 7 { 8 id: 'child-1', 9 type: 'span', 10 name: 'First Child', 11 props: {}, 12 children: 'Hello' 13 }, 14 { 15 id: 'child-2', 16 type: 'span', 17 name: 'Second Child', 18 props: {}, 19 children: 'World' 20 } 21 ] 22}

Variable Reference Children

For dynamic text content bound to variables:

tsx
1{ 2 id: 'dynamic-text', 3 type: 'span', 4 name: 'Dynamic Text', 5 props: {}, 6 children: { __variableRef: 'welcomeMessage' } // VariableReference 7}

When rendered, the variable reference is resolved to the variable's value.

Empty Children

For self-closing elements:

tsx
1{ 2 id: 'line-break', 3 type: 'br', 4 name: 'Line Break', 5 props: {}, 6 children: [] // empty array 7}

Best Practices

Unique IDs

Ensure every layer has a unique id:

tsx
1// ✅ Good - unique IDs 2{ id: 'header-logo', type: 'img', ... } 3{ id: 'nav-menu', type: 'nav', ... } 4{ id: 'footer-copyright', type: 'p', ... } 5 6// ❌ Bad - duplicate IDs 7{ id: 'button', type: 'Button', ... } 8{ id: 'button', type: 'Button', ... } // Duplicate!

Meaningful Names

Use descriptive names for the layers panel:

tsx
1// ✅ Good - descriptive names 2{ id: 'hero-cta', name: 'Hero Call-to-Action', type: 'Button', ... } 3{ id: 'product-grid', name: 'Product Grid', type: 'div', ... } 4 5// ❌ Bad - generic names 6{ id: 'button-1', name: 'Button', type: 'Button', ... } 7{ id: 'div-2', name: 'div', type: 'div', ... }

Component Dependencies

Make sure all referenced component types exist in your registry:

tsx
1// If your Button has span children, include span in registry 2const registry = { 3 ...primitiveComponentDefinitions, // includes 'span' 4 Button: { /* your button definition */ } 5};

Semantic Structure

Follow HTML semantic structure where possible:

tsx
1// ✅ Good - semantic structure 2{ 3 id: 'article', 4 type: 'article', 5 name: 'Blog Post', 6 children: [ 7 { id: 'title', type: 'h1', name: 'Title', ... }, 8 { id: 'meta', type: 'div', name: 'Meta Info', ... }, 9 { id: 'content', type: 'div', name: 'Content', ... } 10 ] 11}