Component Definitions

Learn how to define UI components for use with UICP, including schema creation, validation, and best practices.

Anatomy of a Component

A UICP component consists of three main parts:

1. Schema Definition

Using Zod to define the shape and validation rules for component props.

2. React Component

The actual UI component that receives validated props.

3. Registry Entry

Metadata connecting the schema, component, and description.

Creating a Schema

Use Zod to define your component's props with full type safety:

import { z } from 'zod'

export const ChartSchema = z.object({
  // Required fields
  title: z.string(),
  data: z.array(z.object({
    label: z.string(),
    value: z.number(),
  })),
  
  // Optional fields with defaults
  type: z.enum(['bar', 'line', 'pie']).default('bar'),
  color: z.string().default('#8b5cf6'),
  
  // Complex nested objects
  options: z.object({
    showLegend: z.boolean().default(true),
    animationDuration: z.number().default(300),
  }).optional(),
})

export type ChartProps = z.infer<typeof ChartSchema>

Building the Component

Create a React component that uses the inferred types:

export function Chart({ title, data, type, color, options }: ChartProps) {
  return (
    <div className="border rounded-lg p-6">
      <h3 className="text-xl font-semibold mb-4">{title}</h3>
      {/* Render chart based on type */}
      {type === 'bar' && <BarChart data={data} color={color} />}
      {type === 'line' && <LineChart data={data} color={color} />}
      {type === 'pie' && <PieChart data={data} />}
      
      {options?.showLegend && <Legend data={data} />}
    </div>
  )
}

Registering the Component

Add your component to the registry with descriptive metadata:

import { ComponentRegistry } from '@uicp/tools'

const registry = new ComponentRegistry()

registry.register({
  name: 'Chart',
  component: Chart,
  schema: ChartSchema,
  description: 'Display data visualizations including bar, line, and pie charts',
  category: 'data-visualization',
  examples: [
    {
      title: 'Simple Bar Chart',
      props: {
        title: 'Monthly Sales',
        data: [
          { label: 'Jan', value: 100 },
          { label: 'Feb', value: 150 },
        ],
        type: 'bar',
      },
    },
  ],
})

Best Practices

  • Provide Defaults: Use .default() for optional props
  • Clear Descriptions: Help AI understand when to use each component
  • Validation Rules: Use Zod's built-in validators (email, url, min, max)
  • Examples: Include usage examples to guide AI generation
  • Categories: Group related components for better discovery

Advanced Schemas

Handle complex data structures:

// Union types
const status = z.enum(['success', 'warning', 'error'])

// Discriminated unions
const action = z.discriminatedUnion('type', [
  z.object({ type: z.literal('button'), onClick: z.string() }),
  z.object({ type: z.literal('link'), href: z.string() }),
])

// Recursive schemas
const TreeNode: z.ZodType<any> = z.lazy(() =>
  z.object({
    label: z.string(),
    children: z.array(TreeNode).optional(),
  })
)

Next: Learn about UI Options Tooling to integrate components with AI agents.