| title | Component Registry |
|---|
The Component Registry is Object UI's system for mapping schema types to React components. Understanding the registry is key to extending Object UI with custom components.
The registry acts as a lookup table that the SchemaRenderer uses to determine which React component to render for each schema type:
Schema Type → Component Registry → React Component
import { getComponentRegistry } from '@object-ui/react'
const registry = getComponentRegistry()The easiest way to get started is to register all default components:
import { registerDefaultRenderers } from '@object-ui/components'
// Call once at app initialization
registerDefaultRenderers()This registers all built-in components like:
- Forms:
input,textarea,select,checkbox, etc. - Data:
table,list,card,tree, etc. - Layout:
page,grid,flex,container, etc. - Feedback:
alert,dialog,toast, etc.
Register specific components one at a time:
import { getComponentRegistry } from '@object-ui/react'
import { InputRenderer } from '@object-ui/components'
const registry = getComponentRegistry()
registry.register('input', InputRenderer)Create and register your own components:
import { getComponentRegistry } from '@object-ui/react'
import type { BaseSchema } from '@object-ui/core'
interface MyComponentSchema extends BaseSchema {
type: 'my-component'
title: string
content: string
}
function MyComponent(props: MyComponentSchema) {
return (
<div className="my-component">
<h3>{props.title}</h3>
<p>{props.content}</p>
</div>
)
}
const registry = getComponentRegistry()
registry.register('my-component', MyComponent)Now you can use it in schemas:
{
"type": "my-component",
"title": "Hello",
"content": "This is my custom component!"
}All registered components receive the schema as props:
interface ComponentProps<T extends BaseSchema = BaseSchema> {
// The complete schema object
schema: T
// Data context (optional)
data?: Record<string, any>
// Event handlers (optional)
onAction?: (action: any, context: any) => void
onChange?: (value: any) => void
onSubmit?: (data: any) => void
}
function MyRenderer(props: ComponentProps<MyComponentSchema>) {
const { schema, data, onChange } = props
return (
<div className={schema.className}>
{/* Your component implementation */}
</div>
)
}Register components with additional metadata:
registry.register('my-component', MyComponent, {
displayName: 'My Custom Component',
category: 'Custom',
icon: 'component-icon',
description: 'A custom component for special use cases',
schema: {
type: 'object',
properties: {
title: { type: 'string' },
content: { type: 'string' }
}
}
})This metadata is used by the Visual Designer to provide better editing experience.
Register components that load on demand:
import { lazy } from 'react'
const HeavyComponent = lazy(() => import('./HeavyComponent'))
registry.register('heavy-component', HeavyComponent, {
lazy: true
})Override default components with your own:
import { registerDefaultRenderers } from '@object-ui/components'
// Register defaults first
registerDefaultRenderers()
// Override specific component
registry.register('button', MyCustomButton)Default components are organized by category:
- input
- textarea
- select
- checkbox
- radio
- switch
- slider
- date-picker
- time-picker
- file-upload
- color-picker- table
- list
- card
- tree
- timeline
- calendar
- kanban- page
- container
- grid
- flex
- tabs
- accordion
- divider
- spacer- alert
- toast
- dialog
- drawer
- popover
- tooltip
- progress
- skeleton
- spinner- menu
- breadcrumb
- pagination
- steps- button
- link
- text
- icon
- image
- video
- badge
- avatarconst types = registry.getRegisteredTypes()
console.log(types) // ['input', 'button', 'form', ...]if (registry.has('my-component')) {
console.log('Component is registered')
}const metadata = registry.getMetadata('input')
console.log(metadata)
// {
// displayName: 'Input',
// category: 'Form',
// icon: 'input-icon',
// ...
// }// main.tsx or App.tsx
import { registerDefaultRenderers } from '@object-ui/components'
registerDefaultRenderers()
function App() {
// Your app code
}import type { BaseSchema } from '@object-ui/core'
interface CustomSchema extends BaseSchema {
type: 'custom'
customProp: string
}
function CustomComponent(props: { schema: CustomSchema }) {
// TypeScript ensures type safety
}Use kebab-case for component types:
- ✅
my-component,custom-button,data-table - ❌
MyComponent,customButton,DataTable
registry.register('rating', RatingComponent, {
displayName: 'Star Rating',
category: 'Form',
icon: 'star',
description: 'A 5-star rating input component',
tags: ['form', 'input', 'rating']
})function MyComponent(props: ComponentProps<MySchema>) {
const { schema } = props
const title = schema.title || 'Default Title'
const content = schema.content || ''
return (
<div>
<h3>{title}</h3>
<p>{content}</p>
</div>
)
}Group related components into plugin packages:
// @my-org/objectui-plugin-charts
import { getComponentRegistry } from '@object-ui/react'
import { BarChart } from './BarChart'
import { LineChart } from './LineChart'
import { PieChart } from './PieChart'
export function registerChartComponents() {
const registry = getComponentRegistry()
registry.register('bar-chart', BarChart)
registry.register('line-chart', LineChart)
registry.register('pie-chart', PieChart)
}Usage:
import { registerDefaultRenderers } from '@object-ui/components'
import { registerChartComponents } from '@my-org/objectui-plugin-charts'
registerDefaultRenderers()
registerChartComponents()Here's a complete example of a custom form component:
import { forwardRef } from 'react'
import { getComponentRegistry } from '@object-ui/react'
import type { BaseSchema } from '@object-ui/core'
import { cn } from '@/lib/utils'
interface RatingSchema extends BaseSchema {
type: 'rating'
name: string
label?: string
maxStars?: number
required?: boolean
disabled?: boolean
onChange?: (value: number) => void
}
const RatingComponent = forwardRef<HTMLDivElement, { schema: RatingSchema }>(
({ schema }, ref) => {
const [value, setValue] = useState(0)
const maxStars = schema.maxStars || 5
const handleClick = (rating: number) => {
if (schema.disabled) return
setValue(rating)
schema.onChange?.(rating)
}
return (
<div ref={ref} className={cn('flex flex-col gap-2', schema.className)}>
{schema.label && (
<label className="text-sm font-medium">
{schema.label}
{schema.required && <span className="text-red-500">*</span>}
</label>
)}
<div className="flex gap-1">
{Array.from({ length: maxStars }).map((_, index) => (
<button
key={index}
type="button"
onClick={() => handleClick(index + 1)}
disabled={schema.disabled}
className={cn(
'text-2xl transition-colors',
index < value ? 'text-yellow-400' : 'text-gray-300',
!schema.disabled && 'hover:text-yellow-300 cursor-pointer'
)}
>
★
</button>
))}
</div>
</div>
)
}
)
RatingComponent.displayName = 'Rating'
// Register the component
const registry = getComponentRegistry()
registry.register('rating', RatingComponent, {
displayName: 'Star Rating',
category: 'Form',
description: 'A star rating input component',
schema: {
type: 'object',
properties: {
name: { type: 'string' },
label: { type: 'string' },
maxStars: { type: 'number', default: 5 },
required: { type: 'boolean' },
disabled: { type: 'boolean' }
},
required: ['name']
}
})
export { RatingComponent }- Expression System - Learn about dynamic expressions
- Schema Rendering - Understand the rendering engine
- Custom Plugin Development - Deep dive into component creation
@object-ui/coreREADME - Component registry API@object-ui/reactREADME - React integration- Schema Type Reference - Component metadata reference