The component registry is the heart of UI Builder. It defines which React components are available in the visual editor and how they should be configured. Understanding the registry is essential for using UI Builder effectively with your own components.
What is the Component Registry?
The component registry is a TypeScript object that maps component type names to their definitions. It tells UI Builder:
- How to render the component in the editor
- What properties it accepts and their types
- How to generate forms for editing those properties
- Import paths for code generation
tsx1import { ComponentRegistry } from '@/components/ui/ui-builder/types'; 2 3const myComponentRegistry: ComponentRegistry = { 4 // Complex component with React component 5 'Button': { 6 component: Button, // React component 7 schema: z.object({...}), // Zod schema for props 8 from: '@/components/ui/button' // Import path 9 }, 10 // Primitive component (no React component needed) 11 'span': { 12 schema: z.object({...}) // Just the schema 13 } 14};
Registry Structure
Each registry entry can have these properties:
Required Properties
schema
: Zod schema defining the component's props and their typescomponent
: The React component (required for complex components)from
: Import path for code generation (required for complex components)
Optional Properties
isFromDefaultExport
: Boolean, use default export in generated codefieldOverrides
: Object mapping prop names to custom form fieldsdefaultChildren
: Array of ComponentLayer objects or stringdefaultVariableBindings
: Array of automatic variable bindings
Two Types of Components
Primitive Components
HTML elements that don't need a React component:
tsx1span: { 2 schema: z.object({ 3 className: z.string().optional(), 4 children: z.any().optional(), 5 }) 6 // No 'component' or 'from' needed 7}
Complex Components
Custom React components that need to be imported:
tsx1Button: { 2 component: Button, 3 schema: z.object({ 4 className: z.string().optional(), 5 children: z.any().optional(), 6 variant: z.enum(['default', 'destructive']).default('default'), 7 }), 8 from: '@/components/ui/button' 9}
Pre-built Component Definitions
UI Builder includes example component definitions for testing and getting started:
tsx1import { primitiveComponentDefinitions } from '@/lib/ui-builder/registry/primitive-component-definitions'; 2import { complexComponentDefinitions } from '@/lib/ui-builder/registry/complex-component-definitions'; 3 4const componentRegistry: ComponentRegistry = { 5 ...primitiveComponentDefinitions, // div, span, h1, h2, h3, p, ul, ol, li, img, iframe, a 6 ...complexComponentDefinitions, // Button, Badge, Card, Icon, Flexbox, Grid, Markdown, etc. 7};
Available Pre-built Components:
Primitive Components:
- Layout:
div
,span
- Typography:
h1
,h2
,h3
,p
- Lists:
ul
,ol
,li
- Media:
img
,iframe
- Navigation:
a
(links)
Complex Components:
- Layout:
Flexbox
,Grid
- Content:
Markdown
,CodePanel
- UI Elements:
Button
,Badge
- Advanced:
Card
,Icon
,Accordion
Simple Registry Example
Here's a minimal registry with one custom component:
tsx1import { z } from 'zod'; 2import { Alert } from '@/components/ui/alert'; 3import { primitiveComponentDefinitions } from '@/lib/ui-builder/registry/primitive-component-definitions'; 4import { commonFieldOverrides } from '@/lib/ui-builder/registry/form-field-overrides'; 5 6const myComponentRegistry: ComponentRegistry = { 7 // Include primitive components for basic HTML elements 8 ...primitiveComponentDefinitions, 9 10 // Add your custom component 11 Alert: { 12 component: Alert, 13 schema: z.object({ 14 className: z.string().optional(), 15 children: z.any().optional(), 16 variant: z.enum(['default', 'destructive']).default('default'), 17 }), 18 from: '@/components/ui/alert', 19 fieldOverrides: commonFieldOverrides() 20 } 21};
Component Dependencies
Important: Make sure all component types referenced in your defaultChildren
are included in your registry:
tsx1const componentRegistry: ComponentRegistry = { 2 ...primitiveComponentDefinitions, // ← Includes 'span' needed below 3 Button: { 4 component: Button, 5 schema: z.object({...}), 6 from: '@/components/ui/button', 7 // This Button references 'span' in defaultChildren 8 defaultChildren: [{ 9 id: 'btn-text', 10 type: 'span', // ← Must be in registry 11 name: 'Button Text', 12 props: {}, 13 children: 'Click me' 14 }] 15 } 16};
Schema Design Principles
The Zod schema is crucial as it drives the auto-generated form in the properties panel:
tsx1schema: z.object({ 2 // Use .default() values for better UX 3 title: z.string().default('Default Title'), 4 5 // Use coerce for type conversion from strings 6 count: z.coerce.number().default(1), 7 8 // Boolean props become toggle switches 9 disabled: z.boolean().optional(), 10 11 // Enums become select dropdowns 12 variant: z.enum(['default', 'destructive']).default('default'), 13 14 // Special props need field overrides 15 className: z.string().optional(), 16 children: z.any().optional(), 17})
Building Your Own Registry
For production applications, you should create your own component registry with your specific components:
tsx1// Your production registry 2const productionRegistry: ComponentRegistry = { 3 // Add only the components you need 4 MyButton: { /* your button definition */ }, 5 MyCard: { /* your card definition */ }, 6 MyModal: { /* your modal definition */ }, 7 // Include primitives for basic HTML 8 ...primitiveComponentDefinitions, 9};
The pre-built registries are examples to help you understand the system and test quickly, but you should replace them with your own component definitions that match your design system.
Next Steps
Now that you understand the component registry:
- Custom Components - Learn how to add complex custom components with advanced features
- Advanced Component Config - Explore field overrides, default children, and variable bindings
- Variables - Create dynamic content with variable binding
Remember: The registry is just a configuration object. The real power comes from how you design your components and their schemas to create the best editing experience for your users.