> ## Documentation Index
> Fetch the complete documentation index at: https://docs-terra.withunify.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Form Builder Internals

> How the visual form builder manages state with Zustand and renders fields via registry

# Form Builder Internals

> The form builder uses Zustand for state management and a registry pattern for field rendering.

## Architecture Overview

```mermaid theme={null}
flowchart LR
    subgraph UI["Form Builder UI"]
        Canvas[Canvas]
        Toolbox[Toolbox]
        Props[Properties Panel]
    end

    subgraph State["Zustand Store"]
        Schema[schema]
        Selected[selectedFieldId]
        Dirty[isDirty]
    end

    subgraph Render["Field Registry"]
        Reg[registry.tsx]
        Text[TextFieldRenderer]
        Choice[ChoiceFieldRenderer]
        More[...]
    end

    Toolbox -->|addField| State
    Canvas -->|selectField| State
    Props -->|updateField| State
    State --> Canvas
    Reg --> Canvas
```

## Zustand Store

```typescript theme={null}
// src/stores/form-builder-store.ts

interface FormBuilderState {
  // Schema state
  schema: FormSchema;
  selectedFieldId: string | null;
  selectedPageId: string | null;

  // Dirty tracking
  isDirty: boolean;
  lastSavedAt: Date | null;

  // Actions
  setSchema: (schema: FormSchema) => void;
  addField: (pageId: string, field: FormElement) => void;
  updateField: (fieldId: string, updates: Partial<FormElement>) => void;
  removeField: (fieldId: string) => void;
  moveField: (fieldId: string, newIndex: number) => void;
  selectField: (fieldId: string | null) => void;
}

export const useFormBuilder = create<FormBuilderState>((set, get) => ({
  schema: initialSchema,
  selectedFieldId: null,
  isDirty: false,

  addField: (pageId, field) => {
    set((state) => ({
      schema: addFieldToPage(state.schema, pageId, field),
      isDirty: true,
    }));
  },

  updateField: (fieldId, updates) => {
    set((state) => ({
      schema: updateFieldInSchema(state.schema, fieldId, updates),
      isDirty: true,
    }));
  },
  // ...
}));
```

## Field Registry

Instead of a giant switch statement, we use a registry:

```typescript theme={null}
// src/components/engine/registry.tsx

import { TextFieldRenderer } from "./fields/text-field";
import { ChoiceFieldRenderer } from "./fields/choice-field";
import { AddressFieldRenderer } from "./fields/address-field";
// ...

export const fieldRenderers: Record<string, React.ComponentType<FieldProps>> = {
  text: TextFieldRenderer,
  number: NumberFieldRenderer,
  date: DateFieldRenderer,
  choice: ChoiceFieldRenderer,
  address: AddressFieldRenderer,
  files: FilesFieldRenderer,
  group: GroupFieldRenderer,
  repeated: RepeatedFieldRenderer,
  // ... all field types
};

export function renderField(element: FormElement, props: FieldProps) {
  const Renderer = fieldRenderers[element.type];
  if (!Renderer) {
    console.warn(`Unknown field type: ${element.type}`);
    return null;
  }
  return <Renderer element={element} {...props} />;
}
```

## Adding a New Field Type

1. **Define the schema** in `src/types/schema.ts`
2. **Create the renderer** in `src/components/engine/fields/`
3. **Add to registry** in `src/components/engine/registry.tsx`
4. **Add toolbox entry** in `src/components/form-builder/toolbox.tsx`
5. **Add properties panel** in `src/components/form-builder/properties-panel.tsx`

Example: Rating field

```typescript theme={null}
// 1. Schema
export const RatingFieldSchema = BaseElementSchema.extend({
  type: z.literal("rating"),
  maxStars: z.number().default(5),
});

// 2. Renderer
export function RatingFieldRenderer({ element, value, onChange }) {
  return (
    <div className="flex gap-1">
      {Array.from({ length: element.maxStars }).map((_, i) => (
        <button key={i} onClick={() => onChange(i + 1)}>
          {i < value ? "★" : "☆"}
        </button>
      ))}
    </div>
  );
}

// 3. Registry
fieldRenderers.rating = RatingFieldRenderer;
```

***

<CardGroup cols={2}>
  <Card title="Schema Design" icon="file-code" href="/core-systems/forms/schema-design">
    Form schema structure
  </Card>

  <Card title="Logic Engine" icon="code-branch" href="/core-systems/forms/logic-engine">
    Conditional visibility
  </Card>
</CardGroup>
