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. Component System
  2. Components Intro
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.

Loading...
Live Component Registry

Next Steps

Now that you understand the component registry:

  • Shadcn Registry - Use all 54 shadcn/ui components and 124 block templates pre-configured
  • 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.

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
  • childOf: Array of parent component type names this component can be a child of

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};

Parent-Child Constraints with childOf

Some components only make sense as children of specific parent components. For example, AccordionItem should only be a child of Accordion. The childOf property enforces these relationships:

tsx
1AccordionItem: { 2 component: AccordionItem, 3 schema: z.object({ ... }), 4 from: '@/components/ui/accordion', 5 childOf: ['Accordion'] // Can only be added inside Accordion 6}

How childOf Works

  1. Add Component Popover: When adding components, the popover filters out components whose childOf doesn't include the current parent type
  2. Drag and Drop: Dropping a component is blocked if its childOf constraint isn't satisfied by the target parent
  3. Multiple Parents: Some components can be children of multiple parent types:
tsx
1SelectItem: { 2 component: SelectItem, 3 schema: z.object({ ... }), 4 from: '@/components/ui/select', 5 childOf: ['SelectContent', 'SelectGroup'] // Valid in either parent 6}

Common childOf Patterns

ComponentchildOf
AccordionItem['Accordion']
AccordionTrigger['AccordionItem']
CardHeader, CardContent, CardFooter['Card']
TabsList, TabsContent['Tabs']
TabsTrigger['TabsList']
SelectTrigger, SelectContent['Select']
SelectItem['SelectContent', 'SelectGroup']
DialogTrigger, DialogContent['Dialog']

When to Use childOf

Use childOf when:

  • A component is semantically part of a compound component (Accordion, Tabs, etc.)
  • A component depends on parent context/state to function properly
  • Placing the component elsewhere would cause errors or unexpected behavior

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.