# field

Define form fields for data capture

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

Define form fields for capturing user input. Paradoc supports 24 field types covering text, numeric, temporal, entity, spatial, and selection inputs.

## Examples

### Creating fields

```ts
// Text field with validation
const nameField = field.text()
  .label('Full Name')
  .description('Enter your legal name')
  .required()
  .minLength(2)
  .maxLength(100)
  .build()

// Enum/select field
const statusField = field.enum()
  .label('Status')
  .options([{ value: 'active' }, { value: 'inactive' }, { value: 'pending' }])
  .default('pending')
  .build()

// Nested fieldset
const addressField = field.fieldset()
  .label('Address')
  .field('street', field.text().label('Street'))
  .field('city', field.text().label('City'))
  .field('zip', field.text().label('ZIP Code'))
  .build()
```

### Adding fields to forms

```ts
// Object pattern
const form = p.form({
  name: 'contact-form',
  fields: {
    name: { type: 'text', label: 'Name', required: true },
    email: { type: 'email', label: 'Email', required: true },
    age: { type: 'number', label: 'Age', min: 0, max: 150 }
  }
})

// Builder pattern
const form = p.form()
  .name('contact-form')
  .field('name', field.text().label('Name').required().build())
  .field('email', field.email().label('Email').required().build())
  .field('age', field.number().label('Age').min(0).max(150).build())
  .build()
```

## Field Types

### Text-based

| Type    | Description          | Builder         |
| ------- | -------------------- | --------------- |
| `text`  | Free-form text input | `field.text()`  |
| `email` | Email address        | `field.email()` |
| `uuid`  | UUID string          | `field.uuid()`  |
| `uri`   | URL/URI              | `field.uri()`   |

### Numeric

| Type         | Description                | Builder              |
| ------------ | -------------------------- | -------------------- |
| `number`     | Numeric input              | `field.number()`     |
| `percentage` | Percentage (0-100)         | `field.percentage()` |
| `rating`     | Rating scale (1-5 default) | `field.rating()`     |
| `money`      | Monetary amount            | `field.money()`      |

### Boolean

| Type      | Description     | Builder           |
| --------- | --------------- | ----------------- |
| `boolean` | Checkbox/toggle | `field.boolean()` |

### Selection

| Type          | Description            | Builder               |
| ------------- | ---------------------- | --------------------- |
| `enum`        | Single-select dropdown | `field.enum()`        |
| `multiselect` | Multi-select           | `field.multiselect()` |

### Temporal

| Type       | Description       | Builder            |
| ---------- | ----------------- | ------------------ |
| `date`     | Date (YYYY-MM-DD) | `field.date()`     |
| `datetime` | Date and time     | `field.datetime()` |
| `time`     | Time only         | `field.time()`     |
| `duration` | ISO 8601 duration | `field.duration()` |

### Entity

| Type             | Description        | Builder                  |
| ---------------- | ------------------ | ------------------------ |
| `person`         | Person information | `field.person()`         |
| `organization`   | Organization info  | `field.organization()`   |
| `identification` | ID document        | `field.identification()` |

### Spatial

| Type         | Description    | Builder              |
| ------------ | -------------- | -------------------- |
| `coordinate` | Lat/lng point  | `field.coordinate()` |
| `bbox`       | Bounding box   | `field.bbox()`       |
| `address`    | Postal address | `field.address()`    |
| `phone`      | Phone number   | `field.phone()`      |

### Container

| Type       | Description             | Builder            |
| ---------- | ----------------------- | ------------------ |
| `fieldset` | Nested fields           | `field.fieldset()` |
| `list`     | Ordered repeated fields | `field.list()`     |

## API

### Field Builder

The `field` function creates field builders:

```ts
field()                     // Returns TextFieldBuilder (default)
field(input: FormField)     // Validates and returns FormField
field.text()                // Returns TextFieldBuilder
field.number()              // Returns NumberFieldBuilder
// ... etc for each field type
```

### Common Builder Methods

All field builders share these methods:

Start with `field.*()`. Each method returns `FieldBuilder` and can be chained.

| Method | Description |
| --- | --- |
| `label(value: string)` | Set human-readable label |
| `description(value: string)` | Set helper text |
| `required(value?: boolean \| string)` | Mark as required (default: true). Accepts expression. |
| `visible(value?: boolean \| string)` | Set visibility (default: true). Accepts expression. |

Terminal methods return `FormField`.

| Method | Description |
| --- | --- |
| `build()` | Build and validate |

### Type-Specific Methods

#### Text / UUID / URI

| Name | Type | Description |
| --- | --- | --- |
| `minLength` | `(value: number) => this` | Minimum character length |
| `maxLength` | `(value: number) => this` | Maximum character length |
| `pattern` | `(value: string) => this` | Regex pattern for validation |
| `default` | `(value: string) => this` | Default value |

#### Email

Email fields have no `pattern`: the email format is the constraint.

| Name | Type | Description |
| --- | --- | --- |
| `minLength` | `(value: number) => this` | Minimum character length |
| `maxLength` | `(value: number) => this` | Maximum character length |
| `default` | `(value: string) => this` | Default value |

#### Number

| Name | Type | Description |
| --- | --- | --- |
| `min` | `(value: number) => this` | Minimum allowed value |
| `max` | `(value: number) => this` | Maximum allowed value |
| `step` | `(value: number) => this` | Allowed increment: a value must be a multiple of step (e.g., 0.01 for cents) |
| `default` | `(value: number) => this` | Default value |

#### Money

| Name | Type | Description |
| --- | --- | --- |
| `min` | `(value: number) => this` | Minimum amount |
| `max` | `(value: number) => this` | Maximum amount |
| `currency` | `(value: string) => this` | ISO 4217 alpha-3 code a value must use (e.g., USD). Omit to accept any currency |
| `default` | `(value: Money) => this` | Default value ({ amount, currency }) |

#### Boolean

| Name | Type | Description |
| --- | --- | --- |
| `default` | `(value: boolean) => this` | Default value |

#### Enum

| Name | Type | Description |
| --- | --- | --- |
| `options` | `(values: EnumOption[]) => this` | Available options |
| `default` | `(value: string \| number) => this` | Default selection |

#### Multiselect

| Name | Type | Description |
| --- | --- | --- |
| `options` | `(values: EnumOption[]) => this` | Available options |
| `min` | `(value: number) => this` | Minimum selections required |
| `max` | `(value: number) => this` | Maximum selections allowed |
| `default` | `(value: EnumOptionValue[]) => this` | Default selections |

#### Date / Datetime / Time

| Name | Type | Description |
| --- | --- | --- |
| `min` | `(value: string) => this` | Minimum date/time (ISO format) |
| `max` | `(value: string) => this` | Maximum date/time (ISO format) |
| `default` | `(value: string) => this` | Default value (ISO format) |

#### Percentage

| Name | Type | Description |
| --- | --- | --- |
| `min` | `(value: number) => this` | Minimum percentage |
| `max` | `(value: number) => this` | Maximum percentage |
| `precision` | `(value: number) => this` | Decimal places |
| `default` | `(value: number) => this` | Default value |

#### Rating

| Name | Type | Description |
| --- | --- | --- |
| `min` | `(value: number) => this` | Minimum rating |
| `max` | `(value: number) => this` | Maximum rating |
| `step` | `(value: number) => this` | Rating increment |
| `default` | `(value: number) => this` | Default value |

#### Identification

| Name | Type | Description |
| --- | --- | --- |
| `allowedTypes` | `(...types: string[]) => this` | Allowed ID document types |
| `default` | `(value: Identification) => this` | Default value |

#### Fieldset

| Name | Type | Description |
| --- | --- | --- |
| `field` | `(id: string, field: FormField \| FieldBuilder) => this` | Add one nested field |
| `fields` | `(fields: Record<string, FormField \| FieldBuilder>) => this` | Set nested field definitions |

### Static Methods

| Method | Returns | Description |
| --- | --- | --- |
| `parse(input: unknown)` | `FormField` | Parse unknown input (throws on error) |
| `safeParse(input: unknown)` | `Result<FormField>` | Parse unknown input (returns result object) |

## Common Properties

All fields share these base properties:

| Name | Type | Description |
| --- | --- | --- |
| `type` | `string` | Field type discriminator |
| `label?` | `string` | Human-readable label |
| `description?` | `string` | Helper text displayed in UI |
| `required?` | `boolean \| string` | Whether field is required. Can be an expression. |
| `visible?` | `boolean \| string` | Whether field is visible. Can be an expression. |

## Conditional Logic

The `required` and `visible` properties can be expressions that reference defs keys or field values (`fields.<id>`):

```ts
const form = p.form()
  .name('age-form')
  .def('isAdult', 'fields.age >= 18')
  .field('age', field.number().label('Age').required().build())
  .field('drivingLicense', field.text()
    .label('Driving License')
    .visible('isAdult')
    .required('isAdult')
    .build())
  .build()
```

`visible` and `required` compose into one effective status that cascades through fieldsets: a hidden fieldset hides its whole subtree, and a field is only required when it is also visible. A parent's `required` does not force its children. See [Logic](/concepts/logic) for the expression language.

## Related

* [form](/sdk/artifacts/form) - Parent form artifact
* [Field Schema](/schemas/artifacts/form/field)
