Learn how to connect variables to component properties through the UI and programmatically. This page focuses on the binding mechanicsβsee Variables for fundamentals and Data Binding for external data integration.
Variable binding replaces static property values with dynamic references. When bound, a component property stores a variable reference object:
tsx1// Before binding - static value 2const button = { 3 props: { 4 children: 'Click me', 5 disabled: false 6 } 7}; 8 9// After binding - variable references 10const button = { 11 props: { 12 children: { __variableRef: 'button-text-var' }, 13 disabled: { __variableRef: 'is-loading-var' } 14 } 15};
π‘ See it in action: The demo above shows variable bindings with real-time value resolution from the working example.
Bound properties are visually distinct:
userName β’ string)To remove a variable binding:
Note: Immutable bindings (π) cannot be unbound through the UI.
Here's the actual structure from our live demo showing real variable bindings:
tsx1// Page structure with variable bindings 2const page: ComponentLayer = { 3 id: "variables-demo-page", 4 type: "div", 5 props: { 6 className: "max-w-4xl mx-auto p-8 space-y-8" 7 }, 8 children: [ 9 { 10 id: "page-title", 11 type: "h1", 12 props: { 13 className: "text-4xl font-bold text-gray-900", 14 children: { __variableRef: "pageTitle" } // β Variable binding 15 } 16 }, 17 { 18 id: "user-name", 19 type: "span", 20 props: { 21 className: "text-gray-900", 22 children: { __variableRef: "userName" } // β Another binding 23 } 24 }, 25 { 26 id: "primary-button", 27 type: "Button", 28 props: { 29 variant: "default", 30 children: { __variableRef: "buttonText" }, // β Button text binding 31 disabled: { __variableRef: "isLoading" } // β Boolean binding 32 } 33 } 34 ] 35}; 36 37// Variables that match the bindings 38const variables: Variable[] = [ 39 { 40 id: "pageTitle", 41 name: "Page Title", 42 type: "string", 43 defaultValue: "UI Builder Variables Demo" 44 }, 45 { 46 id: "userName", 47 name: "User Name", 48 type: "string", 49 defaultValue: "John Doe" 50 }, 51 { 52 id: "buttonText", 53 name: "Primary Button Text", 54 type: "string", 55 defaultValue: "Click Me!" 56 }, 57 { 58 id: "isLoading", 59 name: "Loading State", 60 type: "boolean", 61 defaultValue: false 62 } 63];
Components can automatically bind to variables when added to the canvas:
tsx1const componentRegistry = { 2 UserProfile: { 3 component: UserProfile, 4 schema: z.object({ 5 userId: z.string().default("user_123"), 6 displayName: z.string().default("John Doe"), 7 email: z.string().email().default("john@example.com") 8 }), 9 from: "@/components/ui/user-profile", 10 defaultVariableBindings: [ 11 { 12 propName: "userId", 13 variableId: "current_user_id", 14 immutable: true // Cannot be unbound 15 }, 16 { 17 propName: "displayName", 18 variableId: "current_user_name", 19 immutable: false // Can be changed 20 } 21 ] 22 } 23};
Immutable bindings prevent accidental unbinding of critical data:
tsx1// Example: Brand-consistent component with locked bindings 2const BrandedButton = { 3 component: Button, 4 schema: z.object({ 5 text: z.string().default("Click Me"), 6 brandColor: z.string().default("#3b82f6"), 7 companyName: z.string().default("Acme Corp") 8 }), 9 defaultVariableBindings: [ 10 { 11 propName: "brandColor", 12 variableId: "company_brand_color", 13 immutable: true // π Locked to maintain brand consistency 14 }, 15 { 16 propName: "companyName", 17 variableId: "company_name", 18 immutable: true // π Company identity protected 19 } 20 // text prop left unbound for content flexibility 21 ] 22};
At runtime, variable references are resolved to actual values:
tsx1// Variable reference in component props 2const buttonProps = { 3 children: { __variableRef: 'welcome-message' }, 4 disabled: { __variableRef: 'is-loading' } 5}; 6 7// Variables definition 8const variables = [ 9 { 10 id: 'welcome-message', 11 name: 'welcomeMessage', 12 type: 'string', 13 defaultValue: 'Welcome!' 14 }, 15 { 16 id: 'is-loading', 17 name: 'isLoading', 18 type: 'boolean', 19 defaultValue: false 20 } 21]; 22 23// Runtime values override defaults 24const variableValues = { 25 'welcome-message': 'Hello, Jane!', 26 'is-loading': true 27}; 28 29// Resolution process: 30// 1. Find variable by ID β 'welcome-message' 31// 2. Use runtime value if provided β 'Hello, Jane!' 32// 3. Fall back to default if no runtime value β 'Welcome!' 33// 4. Final resolved props: { children: 'Hello, Jane!', disabled: true }
π See More: Learn about Data Binding for external data integration and Rendering Pages for LayerRenderer usage.
tsx1import { useLayerStore } from '@/lib/ui-builder/store/layer-store'; 2 3function CustomBindingControl() { 4 const bindPropToVariable = useLayerStore((state) => state.bindPropToVariable); 5 const unbindPropFromVariable = useLayerStore((state) => state.unbindPropFromVariable); 6 const isBindingImmutable = useLayerStore((state) => state.isBindingImmutable); 7 8 const handleBind = () => { 9 // Bind a component's 'title' prop to a variable 10 bindPropToVariable('button-123', 'title', 'page-title-var'); 11 }; 12 13 const handleUnbind = () => { 14 // Check if binding is immutable first 15 if (!isBindingImmutable('button-123', 'title')) { 16 unbindPropFromVariable('button-123', 'title'); 17 } 18 }; 19 20 return ( 21 <div> 22 <button onClick={handleBind}>Bind Title</button> 23 <button onClick={handleUnbind}>Unbind Title</button> 24 </div> 25 ); 26}
tsx1import { isVariableReference } from '@/lib/ui-builder/utils/variable-resolver'; 2 3// Check if a prop value is a variable reference 4const propValue = layer.props.children; 5 6if (isVariableReference(propValue)) { 7 console.log('Bound to variable:', propValue.__variableRef); 8} else { 9 console.log('Static value:', propValue); 10}
In addition to props, you can also bind layer.children directly to variables:
tsx1// Bind layer children to a variable 2const bindChildrenToVariable = useLayerStore((state) => state.bindChildrenToVariable); 3const unbindChildrenFromVariable = useLayerStore((state) => state.unbindChildrenFromVariable); 4 5// Bind 6bindChildrenToVariable('span-123', 'welcome-message-var'); 7 8// Unbind 9unbindChildrenFromVariable('span-123');
This is useful for text layers (span, p, h1, etc.) where the text content needs to be dynamic.
{ __variableRef: 'variable-id' }tsx1// Debug variable bindings in browser dev tools 2const layer = useLayerStore.getState().findLayerById('my-component'); 3console.log('Layer props:', layer?.props); 4 5// Verify variable resolution 6import { resolveVariableReferences } from '@/lib/ui-builder/utils/variable-resolver'; 7 8const resolved = resolveVariableReferences( 9 layer.props, 10 variables, 11 variableValues 12); 13console.log('Resolved props:', resolved);
π Next Steps: Now that you understand variable binding mechanics, explore Data Binding to connect external data sources and Variables for variable management fundamentals.