Components Introduction

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
tsx
1import { 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 types
  • component: 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 code
  • fieldOverrides: Object mapping prop names to custom form fields
  • defaultChildren: Array of ComponentLayer objects or string
  • defaultVariableBindings: Array of automatic variable bindings

Two Types of Components

Primitive Components

HTML elements that don't need a React component:

tsx
1span: { 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:

tsx
1Button: { 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:

tsx
1import { 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:

tsx
1import { 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:

tsx
1const 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:

tsx
1schema: 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:

tsx
1// 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.

Live Component Registry

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.