# Form (/product/blocks/form)



The `form` block is a field set over React Hook Form and Zod, plus the shells that decide where the form appears. One schema drives the client validation and the server parse, so a rule is written once.

## Live

Every field below is the shipped component, running in your browser. The SKU is validated against a pattern, the price against a minimum, and the create action is stubbed to succeed.

_The form block renders live on this page._

## The shape

A form has three parts, and only the middle one is yours to write.

**A shell** decides where the form lives: `FormPage` for a route of its own, `FormModal` for a dialog or a sheet. It sets the mode — add, edit or view — the record being edited, and what happens when the form finishes. Nothing below it needs to know which shell it is inside.

**Your form body** calls `useEntityForm` with a Zod resolver, default values, and a create and an update function. It returns a React Hook Form instance, a submit handler and a submit-and-add-another handler. You then lay out the fields.

**The fields** each take the form's `control` and a field name. `FormInput`, `FormTextarea`, `FormNumberInput`, `FormSelect`, `FormSwitch` and `FormDateTime` cover the ordinary cases; `FormCombobox` and `FormAsyncCombobox` cover a lookup against a long or remote list; `MultiSelectField` and the repeater cover a list of related rows edited in place. A field renders its own label, description and error, and the error text is looked up as a copy path, so a schema can name a message instead of hard-coding English.

## The submit contract

`create` and `update` return a result — an ok with data, or an error carrying a message and an optional code. The block does the rest: a failed create toasts the error and returns focus to the first field, a successful one toasts and calls the shell's completion handler, and add-another resets the form and mints a new id instead of leaving.

Because create is handed a generated id before it runs, a form that uploads a file or writes children can address the record it is about to create.

## View mode

The same body renders read-only when the shell's mode is view — every field disables itself, and the actions disappear. There is no second component and no `readOnly` prop to thread.

- [All blocks](/product/blocks) — What a block is, what it may import, and how your data reaches it.
