# FormField

Field definitions for capturing user input

Canonical URL: https://docs.paradoc.dev/schemas/artifacts/form/field/

FormField is a union type representing all possible field types in a form. Each field type has a `type` discriminator and shares common base properties.

```typescript
type FormField =
  | TextField
  | BooleanField
  | NumberField
  | CoordinateField
  | BboxField
  | MoneyField
  | AddressField
  | PhoneField
  | DurationField
  | EmailField
  | UuidField
  | UriField
  | EnumField
  | DateField
  | DatetimeField
  | TimeField
  | PersonField
  | OrganizationField
  | IdentificationField
  | MultiselectField
  | PercentageField
  | RatingField
  | FieldsetField
  | ListField
```

## BaseField

All field types share these base properties:

| Name | Type | Description |
| --- | --- | --- |
| `label?` | `string` | Human-readable label for the field |
| `description?` | `string` | Long-form description or helper text displayed in the UI |
| `required?` | `boolean \| string` | Whether this field is required. Can be a boolean or logic expression |
| `visible?` | `boolean \| string` | Whether this field is visible. Can be a boolean or logic expression. Defaults to true |

## Field Types

Each field type includes all [BaseField](#basefield) properties plus the type-specific properties listed below. A field with any other key fails validation, and the error names the key.

### TextField

Text input field for string values.

| Name | Type | Description |
| --- | --- | --- |
| `type` | `'text'` | Discriminator for text field type |
| `minLength?` | `number` | Minimum character length (a non-negative integer) |
| `maxLength?` | `number` | Maximum character length (a non-negative integer) |
| `pattern?` | `string` | Regular expression pattern for validation. It must compile and must not be open to catastrophic backtracking (ReDoS) |
| `default?` | `string` | Default value for the field |

### BooleanField

Boolean checkbox/toggle field.

| Name | Type | Description |
| --- | --- | --- |
| `type` | `'boolean'` | Discriminator for boolean field type |
| `default?` | `boolean` | Default value for the field |

### NumberField

Numeric input field.

| Name | Type | Description |
| --- | --- | --- |
| `type` | `'number'` | Discriminator for number field type |
| `min?` | `number` | Minimum allowed value |
| `max?` | `number` | Maximum allowed value |
| `step?` | `number` | Allowed increment: a value must be a multiple of step (for example 0.01 for cents) |
| `default?` | `number` | Default value for the field |

### EnumField

Single-select dropdown field.

| Name | Type | Description |
| --- | --- | --- |
| `type` | `'enum'` | Discriminator for enum field type |
| `enum` | `{ value: string \| number; label?: string }[]` | Available options for selection; each option value must be distinct |
| `default?` | `string \| number` | Default value for the field |

### MultiselectField

Multi-select field allowing multiple selections.

| Name | Type | Description |
| --- | --- | --- |
| `type` | `'multiselect'` | Discriminator for multiselect field type |
| `enum` | `{ value: string \| number; label?: string }[]` | Available options for selection; each option value must be distinct |
| `min?` | `number` | Minimum number of selections required (a non-negative integer) |
| `max?` | `number` | Maximum number of selections allowed (a non-negative integer) |
| `default?` | `EnumOptionValue[]` | Default selections; each must equal one option value |

### DateField

Date input field (ISO 8601 format: YYYY-MM-DD).

| Name | Type | Description |
| --- | --- | --- |
| `type` | `'date'` | Discriminator for date field type |
| `min?` | `string` | Minimum allowed date in ISO 8601 format |
| `max?` | `string` | Maximum allowed date in ISO 8601 format |
| `default?` | `string` | Default value for the field |

### DatetimeField

Datetime input field (ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ).

| Name | Type | Description |
| --- | --- | --- |
| `type` | `'datetime'` | Discriminator for datetime field type |
| `min?` | `string` | Minimum allowed datetime in ISO 8601 format |
| `max?` | `string` | Maximum allowed datetime in ISO 8601 format |
| `default?` | `string` | Default value for the field |

### TimeField

Time input field (HH:MM:SS format).

| Name | Type | Description |
| --- | --- | --- |
| `type` | `'time'` | Discriminator for time field type |
| `min?` | `string` | Minimum allowed time |
| `max?` | `string` | Maximum allowed time |
| `default?` | `string` | Default value for the field |

### EmailField

Email address input field.

| Name | Type | Description |
| --- | --- | --- |
| `type` | `'email'` | Discriminator for email field type |
| `minLength?` | `number` | Minimum character length (a non-negative integer) |
| `maxLength?` | `number` | Maximum character length (a non-negative integer) |
| `default?` | `string` | Default value for the field |

### UriField

URI/URL input field.

| Name | Type | Description |
| --- | --- | --- |
| `type` | `'uri'` | Discriminator for URI field type |
| `minLength?` | `number` | Minimum character length (a non-negative integer) |
| `maxLength?` | `number` | Maximum character length (a non-negative integer) |
| `pattern?` | `string` | Regular expression pattern for validation. It must compile and must not be open to catastrophic backtracking (ReDoS) |
| `default?` | `string` | Default value for the field |

### UuidField

UUID input field.

| Name | Type | Description |
| --- | --- | --- |
| `type` | `'uuid'` | Discriminator for UUID field type |
| `minLength?` | `number` | Minimum character length (a non-negative integer) |
| `maxLength?` | `number` | Maximum character length (a non-negative integer) |
| `pattern?` | `string` | Regular expression pattern for validation. It must compile and must not be open to catastrophic backtracking (ReDoS) |
| `default?` | `string` | Default value for the field |

### MoneyField

Monetary amount input field.

| Name | Type | Description |
| --- | --- | --- |
| `type` | `'money'` | Discriminator for money field type |
| `min?` | `number` | Minimum allowed amount |
| `max?` | `number` | Maximum allowed amount |
| `currency?` | `string` | ISO 4217 alpha-3 code a value must use (for example USD). Omit to accept any currency |
| `default?` | `Money` | Default value for the field |

### PercentageField

Percentage input field (0-100 by default).

| Name | Type | Description |
| --- | --- | --- |
| `type` | `'percentage'` | Discriminator for percentage field type |
| `min?` | `number` | Minimum allowed percentage |
| `max?` | `number` | Maximum allowed percentage |
| `precision?` | `number` | Number of decimal places (a non-negative integer) |
| `default?` | `number` | Default value for the field |

### RatingField

Rating input field (1-5 by default).

| Name | Type | Description |
| --- | --- | --- |
| `type` | `'rating'` | Discriminator for rating field type |
| `min?` | `number` | Minimum rating value |
| `max?` | `number` | Maximum rating value |
| `step?` | `number` | Rating increment step; must be greater than 0 |
| `default?` | `number` | Default value for the field |

### AddressField

Postal address input field.

| Name | Type | Description |
| --- | --- | --- |
| `type` | `'address'` | Discriminator for address field type |
| `default?` | `Address` | Default value for the field |

### PhoneField

Phone number input field.

| Name | Type | Description |
| --- | --- | --- |
| `type` | `'phone'` | Discriminator for phone field type |
| `default?` | `Phone` | Default value for the field |

### CoordinateField

Geographic coordinate (latitude/longitude) input field.

| Name | Type | Description |
| --- | --- | --- |
| `type` | `'coordinate'` | Discriminator for coordinate field type |
| `default?` | `Coordinate` | Default value for the field |

### BboxField

Geographic bounding box input field.

| Name | Type | Description |
| --- | --- | --- |
| `type` | `'bbox'` | Discriminator for bbox field type |
| `default?` | `Bbox` | Default value for the field |

### DurationField

Duration input field (ISO 8601 format).

| Name | Type | Description |
| --- | --- | --- |
| `type` | `'duration'` | Discriminator for duration field type |
| `default?` | `Duration` | Default value for the field |

### PersonField

Person information input field.

| Name | Type | Description |
| --- | --- | --- |
| `type` | `'person'` | Discriminator for person field type |
| `default?` | `Person` | Default value for the field |

### OrganizationField

Organization information input field.

| Name | Type | Description |
| --- | --- | --- |
| `type` | `'organization'` | Discriminator for organization field type |
| `default?` | `Organization` | Default value for the field |

### IdentificationField

Identification document input field.

| Name | Type | Description |
| --- | --- | --- |
| `type` | `'identification'` | Discriminator for identification field type |
| `allowedTypes?` | `string[]` | Allowed identification document types |
| `default?` | `Identification` | Default value for the field |

### ListField

A repeatable list whose `item` is another `FormField`. See [ListField](/schemas/artifacts/form/list).

### FieldsetField

Nested fieldset containing other fields.

| Name | Type | Description |
| --- | --- | --- |
| `type` | `'fieldset'` | Discriminator for fieldset field type |
| `fields` | `Record<string, FormField>` | Nested field definitions keyed by identifier |

## Examples



**YAML**

```yaml
fields:
  # Text field with validation
  firstName:
    type: text
    label: First Name
    required: true
    minLength: 1
    maxLength: 100

  # Email field
  email:
    type: email
    label: Email Address
    required: true

  # Enum dropdown
  preferredContact:
    type: enum
    label: Preferred Contact Method
    enum: [{ value: "email" }, { value: "phone" }, { value: "mail" }]
    default: email

  # Money field
  annualIncome:
    type: money
    label: Annual Income
    required: true
    min: 0

  # Date with constraints
  birthDate:
    type: date
    label: Date of Birth
    max: "2006-01-01"

  # Conditional field
  maritalStatus:
    type: enum
    enum:
      - value: single
        label: Single
      - value: married
        label: Married
  spouseName:
    type: person
    label: Spouse Information
    visible: "fields.maritalStatus == 'married'"
    required: "fields.maritalStatus == 'married'"

  # Nested fieldset
  emergencyContact:
    type: fieldset
    label: Emergency Contact
    fields:
      name:
        type: text
        label: Contact Name
        required: true
      phone:
        type: phone
        label: Contact Phone
        required: true
```

**JSON**

```json
{
  "fields": {
    "firstName": {
      "type": "text",
      "label": "First Name",
      "required": true,
      "minLength": 1,
      "maxLength": 100
    },
    "email": {
      "type": "email",
      "label": "Email Address",
      "required": true
    },
    "preferredContact": {
      "type": "enum",
      "label": "Preferred Contact Method",
      "enum": [{ "value": "email" }, { "value": "phone" }, { "value": "mail" }],
      "default": "email"
    },
    "annualIncome": {
      "type": "money",
      "label": "Annual Income",
      "required": true,
      "min": 0
    },
    "birthDate": {
      "type": "date",
      "label": "Date of Birth",
      "max": "2006-01-01"
    },
    "maritalStatus": {
      "type": "enum",
      "enum": [
        { "value": "single", "label": "Single" },
        { "value": "married", "label": "Married" }
      ]
    },
    "spouseName": {
      "type": "person",
      "label": "Spouse Information",
      "visible": "fields.maritalStatus == 'married'",
      "required": "fields.maritalStatus == 'married'"
    },
    "emergencyContact": {
      "type": "fieldset",
      "label": "Emergency Contact",
      "fields": {
        "name": {
          "type": "text",
          "label": "Contact Name",
          "required": true
        },
        "phone": {
          "type": "phone",
          "label": "Contact Phone",
          "required": true
        }
      }
    }
  }
}
```

## Visibility cascade

`visible` and `required` compose into one effective status (`hidden < optional < required`), and that status cascades through fieldsets:

* A hidden fieldset hides its whole subtree. Children of a hidden container are hidden regardless of their own `visible`.
* `required` follows effective visibility: a field is only required if it is also visible. A hidden field is never required.
* A parent's `required` does not force its children. An optional fieldset can still hold required children, and a required fieldset does not make its children required.

So you author two conditions (`visible`, `required`), and the engine resolves them into a single status per field, with hidden winning over everything below it.

## Related

* [Form](/schemas/artifacts/form) - Form artifact overview
* [Logic](/concepts/logic) - The expression language for `visible` / `required`
* [Primitives](/schemas/primitives) - Primitive types used as field defaults
