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; // Child layers or text content 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: Either an array of child layers or a string for text content

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}

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}

Related Topics