

Form Builder turns a typed Zod schema and a `formDefs` tree into Cubitt form UI. Use `FormBuilder.Single` for one record and `FormBuilder.Bulk` when the same definitions need CSV import, column mapping, grid editing, and row validation.

## Usage [#usage]

```tsx
import {
  FormBuilder,
  type FormDefs,
} from "@tilt-legal/cubitt-components/form-builder";
```

```tsx
const formDefs = [
  {
    kind: "field",
    name: "email",
    label: "Email",
    component: "email",
    size: "full",
  },
] as const satisfies FormDefs<Member>;
```

<Cards>
  <Card title="Single" description="Render a schema-driven form for one record." href="./single" />

  <Card title="Bulk" description="Import CSV rows, review mapping, edit inline, and submit validated rows." href="./bulk" />

  <Card title="Form Definitions" description="Reference for steps, groups, fields, options, and conditional predicates." href="./form-defs" />

  <Card title="Validation" description="Client, server, row, and group-level validation patterns." href="./validation" />
</Cards>

## Core Model [#core-model]

```tsx
import { z } from "zod";
import type { FormDefs } from "@tilt-legal/cubitt-components/form-builder";

const memberSchema = z.object({
  firstName: z.string().min(1, "Required"),
  lastName: z.string().min(1, "Required"),
  email: z.string().email("Invalid email"),
});

type Member = z.infer<typeof memberSchema>;

const memberFormDefs = [
  {
    kind: "field",
    name: "firstName",
    label: "First name",
    component: "text",
    size: "half",
  },
  {
    kind: "field",
    name: "lastName",
    label: "Last name",
    component: "text",
    size: "half",
  },
  {
    kind: "field",
    name: "email",
    label: "Email",
    component: "email",
    size: "full",
  },
] as const satisfies FormDefs<Member>;
```

## Utilities [#utilities]

| Utility              | Description                                             |
| -------------------- | ------------------------------------------------------- |
| `useFormBuilder`     | Low-level hook for single-record TanStack forms         |
| `useBulkFormBuilder` | Low-level hook for bulk row validation                  |
| `injectZodErrors`    | Inject Zod issues into field errors                     |
| `injectFormError`    | Add form-level error messages                           |
| `flattenFormDefs`    | Flatten nested definitions into field metadata          |
| `shapeRows`          | Coerce CSV rows using field metadata                    |
| `mapZodError`        | Map Zod issues to field paths                           |
| `FieldRenderer`      | Render a field from a definition inside a field context |
