# Form Fields URL: /docs/web/components/form-fields Dynamic form field system powered by TanStack Form. 49 field types with automatic dispatch via DynamicFormField. **Demo:** ```tsx 'use client'; // @custom-demo import { lazy } from 'react'; import { ApprovalStatusFormField } from '@docyrus/ui/components/form-fields/approval-status-form-field'; import { AvatarField } from '@docyrus/ui/components/form-fields/avatar-field'; import { CheckboxFormField } from '@docyrus/ui/components/form-fields/checkbox-form-field'; import { CheckboxGroupFormField } from '@docyrus/ui/components/form-fields/checkbox-group-form-field'; import { CodeEditorFormField } from '@docyrus/ui/components/form-fields/code-editor-form-field'; import { ColorFormField } from '@docyrus/ui/components/form-fields/color-form-field'; import { CurrencyCodeFormField } from '@docyrus/ui/components/form-fields/currency-code-form-field'; import { DateFormField } from '@docyrus/ui/components/form-fields/date-form-field'; import { DateRangeFormField } from '@docyrus/ui/components/form-fields/date-range-form-field'; import { DateTimeFormField } from '@docyrus/ui/components/form-fields/datetime-form-field'; import { DurationFormField } from '@docyrus/ui/components/form-fields/duration-form-field'; import { EmailFormField } from '@docyrus/ui/components/form-fields/email-form-field'; import { EnumFormField } from '@docyrus/ui/components/form-fields/enum-form-field'; import { FieldMappingFormField } from '@docyrus/ui/components/form-fields/field-mapping-form-field'; import { FileFormField } from '@docyrus/ui/components/form-fields/file-form-field'; import { AdaptiveCardFormField } from '@docyrus/ui/components/form-fields/adaptive-card-form-field'; import { HandlebarsFormField } from '@docyrus/ui/components/form-fields/handlebars-form-field'; import { IconFormField } from '@docyrus/ui/components/form-fields/icon-form-field'; import { ImageFormField } from '@docyrus/ui/components/form-fields/image-form-field'; import { JsonSchemaFormField } from '@docyrus/ui/components/form-fields/json-schema-form-field'; import { JsonataFormField } from '@docyrus/ui/components/form-fields/jsonata-form-field'; import { LocationSelectFormField } from '@docyrus/ui/components/form-fields/location-select-form-field'; import { MarkdownEditorFormField } from '@docyrus/ui/components/form-fields/markdown-editor-form-field'; import { MoneyFormField } from '@docyrus/ui/components/form-fields/money-form-field'; import { MultiSelectFormField } from '@docyrus/ui/components/form-fields/multi-select-form-field'; import { NumberFormField } from '@docyrus/ui/components/form-fields/number-form-field'; import { PasswordFormField } from '@docyrus/ui/components/form-fields/password-form-field'; import { PercentFormField } from '@docyrus/ui/components/form-fields/percent-form-field'; import { PhoneFormField } from '@docyrus/ui/components/form-fields/phone-form-field'; import { RadioGroupFormField } from '@docyrus/ui/components/form-fields/radio-group-form-field'; import { RatingFormField } from '@docyrus/ui/components/form-fields/rating-form-field'; import { RelationFormField } from '@docyrus/ui/components/form-fields/relation-form-field'; import { SchemaRepeaterFormField } from '@docyrus/ui/components/form-fields/schema-repeater-form-field'; import { SelectFormField } from '@docyrus/ui/components/form-fields/select-form-field'; import { StatusFormField } from '@docyrus/ui/components/form-fields/status-form-field'; import { SwitchFormField } from '@docyrus/ui/components/form-fields/switch-form-field'; import { TagSelectFormField } from '@docyrus/ui/components/form-fields/tag-select-form-field'; import { TreeSelectFormField } from '@docyrus/ui/components/form-fields/tree-select-form-field'; import { TaskListFormField } from '@docyrus/ui/components/form-fields/task-list-form-field'; import { TextFormField } from '@docyrus/ui/components/form-fields/text-form-field'; import { TextareaFormField } from '@docyrus/ui/components/form-fields/textarea-form-field'; import { TimeFormField } from '@docyrus/ui/components/form-fields/time-form-field'; import { UrlFormField } from '@docyrus/ui/components/form-fields/url-form-field'; import { UserMultiSelectFormField } from '@docyrus/ui/components/form-fields/user-multi-select-form-field'; import { UserSelectFormField } from '@docyrus/ui/components/form-fields/user-select-form-field'; const DocEditorFormField = lazy(() => import('@docyrus/ui/components/form-fields/doc-editor-form-field').then(m => ({ default: m.DocEditorFormField }))); const HtmlEditorFormField = lazy(() => import('@docyrus/ui/components/form-fields/html-editor-form-field').then(m => ({ default: m.HtmlEditorFormField }))); import { QueryBuilderFormField } from '@docyrus/ui/components/form-fields/query-builder-form-field'; import { APPROVAL_OPTIONS, AVAILABLE_FIELDS_SCHEMA, CATEGORY_OPTIONS, DEMO_FIELDS, DEPARTMENT_OPTIONS, NESTED_LOCATION_OPTIONS, PRIORITY_OPTIONS, QUERY_BUILDER_FIELD_OPTIONS, RELATION_OPTIONS, SKILL_OPTIONS, STATUS_OPTIONS, TAG_OPTIONS, USER_OPTIONS, mockForm } from '@/data/form-fields-data'; function SectionHeader({ children }: { children: string }) { return (

{children}

); } export function FormFieldsDemo() { return (
{/* Text & Content */}
{/* Numbers */}
{/* Selection */}
{/* Date & Time */}
{/* Toggle */}
{/* Relation & Users */}
{/* File & Media */}
{/* Rich Content */}
{/* EmailEditorFormField temporarily disabled — react-email-editor ESM issue */}
{/* Data Structure */}
{/* Dynamic Values */}

Wrap any form field with FieldMappingFormField to offer four value sources: fixed, field mapping (recursive tree picker over a shared schema), template (Handlebars), and formula (JSONata). The same schema drives autocomplete inside both editors — try customer.address.city or orders[0].product.

); } ``` ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/ui-form-fields ``` **Dependencies:** - [@tanstack/react-form](https://www.npmjs.com/package/@tanstack/react-form) - [lucide-react](https://www.npmjs.com/package/lucide-react) - [date-fns](https://www.npmjs.com/package/date-fns) - [react-day-picker](https://www.npmjs.com/package/react-day-picker) - [platejs](https://www.npmjs.com/package/platejs) - [react-email-editor](https://www.npmjs.com/package/react-email-editor) - [react-querybuilder](https://www.npmjs.com/package/react-querybuilder) - [@uiw/react-codemirror](https://www.npmjs.com/package/@uiw/react-codemirror) - [@uiw/codemirror-extensions-langs](https://www.npmjs.com/package/@uiw/codemirror-extensions-langs) ## Usage ```tsx import { useForm } from '@tanstack/react-form'; import { DynamicFormField } from '@docyrus/ui/components/form-fields'; import type { IField, EnumOption } from '@docyrus/ui/components/form-fields'; const fields: IField[] = [ { id: '1', name: 'Full Name', slug: 'full_name', type: 'field-text' }, { id: '2', name: 'Priority', slug: 'priority', type: 'field-select' }, { id: '3', name: 'Active', slug: 'is_active', type: 'field-switch' }, ]; const enumOptions: EnumOption[] = [ { id: 'low', name: 'Low', color: '#22c55e' }, { id: 'medium', name: 'Medium', color: '#f59e0b' }, { id: 'high', name: 'High', color: '#ef4444' }, ]; function MyForm() { const form = useForm({ defaultValues: { full_name: '', priority: '', is_active: false }, onSubmit: ({ value }) => console.log(value), }); return (
{ e.preventDefault(); form.handleSubmit(); }}> {fields.map((field) => ( ))}
); } ``` ### Individual Field Import You can import individual field components to reduce bundle size: ```tsx import { TextFormField } from '@docyrus/ui/components/form-fields/text-form-field'; import { SelectFormField } from '@docyrus/ui/components/form-fields/select-form-field'; import { SwitchFormField } from '@docyrus/ui/components/form-fields/switch-form-field'; ``` ## Field Types ### Text & Content | Type | Component | Description | |------|-----------|-------------| | `field-text` | `TextFormField` | Single-line text input | | `field-textarea` | `TextareaFormField` | Multi-line text area | | `field-email` | `EmailFormField` | Email input with validation | | `field-url` | `UrlFormField` | URL input with `https://` placeholder | | `field-phone` | `PhoneFormField` | Phone number with mask input | | `field-color` | `ColorFormField` | Color picker with hex input | | `field-icon` | `IconFormField` | Icon picker (Docyrus icon set) | ### Numbers | Type | Component | Description | |------|-----------|-------------| | `field-number` | `NumberFormField` | Numeric input | | `field-money` | `MoneyFormField` | Money input with currency symbol | | `field-percent` | `PercentFormField` | Percentage input (0-100) | | `field-duration` | `DurationFormField` | Duration picker grid via [Duration Select](/docs/web/components/duration-select) | | `field-rating` | `RatingFormField` | Star rating (1-5) | | `field-currency` | `CurrencyCodeFormField` | Currency code selector | ### Selection | Type | Component | Description | |------|-----------|-------------| | `field-select` | `SelectFormField` | Single select with color/icon indicators | | `field-multiSelect` | `MultiSelectFormField` | Multi-value select | | `field-tagSelect` | `TagSelectFormField` | Tag-based multi-select | | `field-radioGroup` | `RadioGroupFormField` | Radio button group | | — | `CheckboxGroupFormField` | Inline multi-select with radio-group visuals — array value, `default` and `card` variants, `columnCount` 1–4 | | `field-enum` | `EnumFormField` | Enum dropdown (slug-based values) | | `field-status` | `StatusFormField` | Status select with description + follow-up | | `field-approvalStatus` | `ApprovalStatusFormField` | Approval workflow status | ### Date & Time | Type | Component | Description | |------|-----------|-------------| | `field-date` | `DateFormField` | Date picker with calendar popover | | `field-dateTime` | `DateTimeFormField` | Combined date and time picker | | `field-time` | `TimeFormField` | Time picker | | `field-dateRange` | `DateRangeFormField` | Date range with start/end | ### Toggle | Type | Component | Description | |------|-----------|-------------| | `field-checkbox` | `CheckboxFormField` | Checkbox with horizontal layout | | `field-switch` | `SwitchFormField` | Toggle switch with horizontal layout | ### Relation & Users | Type | Component | Description | |------|-----------|-------------| | `field-relation` | `RelationFormField` | Related record lookup | | `field-userSelect` | `UserSelectFormField` | Single user selector | | `field-userMultiSelect` | `UserMultiSelectFormField` | Multi-user selector | | `field-locationSelect` | `LocationSelectFormField` | Location/address picker with Google Maps when `VITE_GOOGLE_MAPS_API_KEY` is configured | ### Rich Content > `LocationSelectFormField` automatically upgrades to the map-based picker in Vite apps when `VITE_GOOGLE_MAPS_API_KEY` is available. Without that variable, it falls back to manual address and coordinate inputs. | Type | Component | Description | |------|-----------|-------------| | `field-docEditor` | `DocEditorFormField` | Rich text editor (Plate.js) with preset-based plugin system | | `field-htmlEditor` | `HtmlEditorFormField` | WYSIWYG HTML editor (Plate.js) with export/import support | | `field-emailEditor` | `EmailEditorFormField` | Email template editor (Unlayer) | | `field-codeEditor` | `CodeEditorFormField` | Code editor (CodeMirror 6) with syntax highlighting and dark/light theme | | `field-adaptiveCard` | `AdaptiveCardFormField` | Dialog-based [Adaptive Card Designer](/docs/web/components/adaptive-card-designer) — visual authoring of Microsoft Adaptive Card payloads | ### Data & Structure | Type | Component | Description | |------|-----------|-------------| | `field-file` | `FileFormField` | File upload | | `field-image` | `ImageFormField` | Image upload with preview | | `field-avatar` | `AvatarField` | Avatar selector | | `field-taskList` | `TaskListFormField` | Checklist / task list | | `field-schemaRepeater` | `SchemaRepeaterFormField` | Repeater with drag & drop, collapsible rows, schema or key-value mode | | `field-jsonSchema` | `JsonSchemaFormField` | Dialog-based [JSON Schema Designer](/docs/web/components/json-schema-designer) — drag-and-drop tree + JSON tab | | `field-jsonata` | `JsonataFormField` | Dialog-based [JSONata Editor](/docs/web/components/jsonata-editor) — write, autocomplete and live-test a JSONata expression | | `field-queryBuilder` | `QueryBuilderFormField` | Query builder | | `field-dynamic` | `DynamicConfigFormField` | Dynamic configuration field | ## API Reference ### DynamicFormField Dispatcher component that renders the correct field based on `field.type`. ```tsx ``` ### DocEditorFormField Rich text editor with preset-based plugin and toolbar system. ```tsx import { DocEditorFormField } from '@docyrus/ui/components/form-fields/doc-editor-form-field'; import type { DocEditorPreset } from '@docyrus/ui/components/form-fields/doc-editor-form-field'; ``` | Prop | Type | Default | Description | |------|------|---------|-------------| | `preset` | `DocEditorPreset` | `'default'` | Editor preset level | #### Presets | Preset | Plugins | Toolbar | |--------|---------|---------| | `default` | Paragraphs, headings, lists, inline marks, link, autoformat | Floating only (marks + turn into + link) | | `rich` | + table, code block, callout, toggle, emoji, font, align | Fixed + Floating | | `full` | + columns, math, date, ToC, comments, suggestions, DnD, block menu | Fixed + Floating (all buttons) | #### Preset Dependencies Each preset requires additional packages beyond the base `platejs` dependency: | Preset | Additional Packages | |--------|-------------------| | `default` | `@platejs/autoformat` `@platejs/link` `@platejs/floating` `@platejs/list` | | `rich` | + `@platejs/code-block` `@platejs/table` `@platejs/callout` `@platejs/toggle` `@platejs/basic-styles` `@platejs/emoji` `@platejs/indent` `@emoji-mart/data` `lowlight` | | `full` | + `@platejs/layout` `@platejs/math` `@platejs/date` `@platejs/toc` `@platejs/comment` `@platejs/suggestion` `@platejs/selection` `@platejs/dnd` `react-dnd` `react-dnd-html5-backend` | ### HtmlEditorFormField WYSIWYG HTML editor powered by Plate.js that stores value as an HTML string. Includes the same rich formatting toolbar as DocEditorFormField (bold, italic, headings, lists, tables, etc.) plus built-in export and import buttons. ```tsx import { HtmlEditorFormField } from '@docyrus/ui/components/form-fields/html-editor-form-field'; ``` | Prop | Type | Default | Description | |------|------|---------|-------------| | `field` | `IField` | — | Field configuration | | `form` | `any` | — | TanStack Form instance | | `disabled` | `boolean` | `false` | Disable editing | | `className` | `string` | — | Additional CSS class | | `preset` | `DocEditorPreset` | `'rich'` | Plate.js preset level | **Data format:** Unlike DocEditorFormField (which stores Plate JSON), HtmlEditorFormField stores the value as an HTML string. Plate content is serialized to HTML via `serializeHtml` and deserialized back via `editor.api.html.deserialize`. **Export:** Built-in export toolbar button supports HTML, PDF, Image, and Markdown formats. **Import:** Built-in import toolbar button supports HTML and Markdown file import. ### CodeEditorFormField Code editor powered by CodeMirror 6 with syntax highlighting, bracket matching, and autocompletion. Supports dark/light theme via `useDocyTheme`. ```tsx import { CodeEditorFormField } from '@docyrus/ui/components/form-fields/code-editor-form-field'; ``` | Prop | Type | Default | Description | |------|------|---------|-------------| | `field` | `IField` | — | Field configuration | | `form` | `any` | — | TanStack Form instance | | `disabled` | `boolean` | `false` | Disable editing | | `className` | `string` | — | Additional CSS class | | `language` | `LanguageName` | `'tsx'` | CodeMirror language (`'tsx'`, `'python'`, `'json'`, `'css'`, `'sql'`, `'go'`, `'rust'`, etc.) | | `basicSetup` | `BasicSetupOptions` | See below | CodeMirror basicSetup overrides | | `minHeight` | `string` | `'200px'` | Editor minimum height | **Default basicSetup:** Line numbers, fold gutter, bracket matching, close brackets, autocompletion, active line highlight, selection match highlight, tab size 2. **Language support:** 50+ languages via `@uiw/codemirror-extensions-langs`. Dark/light theme auto-detected via `useDocyTheme`. ### SchemaRepeaterFormField Repeatable form rows with drag & drop reordering, collapsible items, and dual mode support. Inspired by [Filament Repeater](https://filamentphp.com/docs/3.x/forms/fields/repeater). **Schema mode** — pass `schema` prop with `IField[]` to render sub-fields per row: ```tsx import { SchemaRepeaterFormField } from '@docyrus/ui/components/form-fields/schema-repeater-form-field'; ``` **Key-value mode** — omit `schema` prop for simple key-value pairs: ```tsx ``` | Prop | Type | Default | Description | |------|------|---------|-------------| | `field` | `IField` | — | Field configuration | | `form` | `any` | — | TanStack Form instance | | `disabled` | `boolean` | `false` | Disable editing | | `className` | `string` | — | Additional CSS class | | `schema` | `IField[]` | — | Sub-field definitions. If omitted, key-value mode. | | `schemaEnumOptions` | `Record` | — | Enum options keyed by sub-field slug | | `maxItems` | `number` | — | Maximum number of items | | `minItems` | `number` | `0` | Minimum number of items | | `collapsible` | `boolean` | `true` | Whether items can be collapsed | | `defaultCollapsed` | `boolean` | `false` | Whether items start collapsed | | `addLabel` | `string` | `'Add item'` | Add button label | | `cloneable` | `boolean` | `false` | Whether items can be cloned | | `itemLabel` | `(item, index) => string` | `#index` | Dynamic item label | ### FieldMappingFormField Wrapper that provides four value sources for any form field: **fixed**, **field mapping**, **template** (with `@mention`), and **formula**. The active tab is auto-detected from the stored value prefix. If only one tab is enabled via `modes`, the wrapper renders without the tab chrome. ```tsx import { FieldMappingFormField } from '@docyrus/ui/components/form-fields/field-mapping-form-field'; import type { AvailableField } from '@docyrus/ui/components/form-fields/field-mapping-form-field'; const availableFields: AvailableField[] = [ { name: 'user.name', label: 'User Name', type: 'text' }, { name: 'user.email', label: 'User Email', type: 'email' }, { name: 'project.title', label: 'Project Title', type: 'text' } ]; ``` **Value format** — the wrapper encodes the active mode as a prefix in the form value: | Mode | Prefix | Example value | |------|--------|---------------| | `fixed` | *(none)* | `Hello world` | | `field-mapping` | `#FIELD=` | `#FIELD=user.name` | | `template` | `#TEMPLATE=` | `#TEMPLATE=Hello @user.name!` | | `formula` | `#FORMULA=` | `#FORMULA=user.age > 18 ? "adult" : "minor"` | | Prop | Type | Default | Description | |------|------|---------|-------------| | `field` | `IField` | — | Field configuration | | `form` | `any` | — | TanStack Form instance | | `disabled` | `boolean` | `false` | Disable all tabs | | `required` | `boolean` | `false` | Show required indicator on label | | `className` | `string` | — | Additional CSS class | | `availableFields` | `AvailableField[]` | `[]` | Selectable fields for mapping/template/formula tabs | | `modes` | `MappingTab[]` | `['fixed', 'field-mapping', 'template', 'formula']` | Which tabs to show | | `children` | `ReactNode` | — | Form field component rendered in the Fixed tab | **AvailableField:** | Field | Type | Description | |-------|------|-------------| | `name` | `string` | Slug used in the encoded value (e.g. `user.name`) | | `label` | `string` | Human-readable label shown in dropdowns and mention spans | | `type` | `string` | Field data type | | `icon` | `string` | Optional icon name | **Template mentions** — inside the Template tab, type `@` to open a searchable picker. Inserted mentions render as styled spans with `data-field-name` attributes. The editor uses strict allowlist parsing (text, `
`, and `data-field-name` spans only) to prevent XSS when reloading stored HTML. ### CheckboxGroupFormField Inline multi-select that shares its visual language with `RadioGroupFormField` (square checkbox indicators) and stores the selected option ids as a `string[]` like `TagSelectFormField`. Good fit for a small, fixed set of options where the dropdown popover of TagSelect would feel heavy. ```tsx import { CheckboxGroupFormField } from '@docyrus/ui/components/form-fields/checkbox-group-form-field'; ``` | Prop | Type | Default | Description | |------|------|---------|-------------| | `field` | `IField` | — | Field configuration. The form value at `field.slug` must be `string[]`. | | `form` | `any` | — | TanStack Form instance | | `enumOptions` | `EnumOption[]` | `[]` | Selectable options. Supports `icon`, `color`, and `description`. | | `variant` | `'dropdown' \| 'card'` | — | `'card'` renders bordered cards with description support. Any other value renders the compact inline layout. | | `columnCount` | `1 \| 2 \| 3 \| 4` | `1` | Number of grid columns, responsive at `sm` / `lg` breakpoints. | | `disabled` | `boolean` | `false` | Disable all options | | `required` | `boolean` | `false` | Show red asterisk on label | | `className` | `string` | — | Additional CSS class on the outer `Field` | **Visual variants** | Variant | Layout | Use case | |---------|--------|----------| | *default* | Compact inline checkbox + label, aligns with `RadioGroup` default | Short labels, dense layouts | | `card` | Bordered card with icon/color + label + description, highlights on check | Rich choices with descriptions or imagery | ### DocyrusFormFieldProps All form field components share this interface: | Prop | Type | Default | Description | |------|------|---------|-------------| | `field` | `IField` | — | Field configuration from the data source | | `form` | `any` | — | TanStack Form instance (`useForm()` result) | | `disabled` | `boolean` | `false` | Whether the field is disabled | | `required` | `boolean` | `false` | Show red asterisk on label | | `className` | `string` | — | Additional CSS class | | `enumOptions` | `EnumOption[]` | `[]` | Options for select-based fields | | `appSlug` | `string` | — | App slug for dynamic enum loading | | `dataSourceSlug` | `string` | — | Data source slug for dynamic enum loading | ### IField | Field | Type | Description | |-------|------|-------------| | `id` | `string` | Unique field identifier | | `name` | `string` | Display label | | `slug` | `string` | Form value key (used as `form.Field` name) | | `type` | `IFieldType` | Field type — determines which component renders | | `defaultValue` | `string \| null` | Default value | | `validations` | `string[] \| null` | Validation rules | | `readOnly` | `boolean \| null` | Read-only flag | | `fieldActions` | `FieldAction[] \| null` | Imperative actions triggered when this field's value changes. Evaluated by `useDocyrusFormView`'s field-actions engine. | | `customValidations` | `CustomValidationRule[] \| null` | JSONata-based validation rules evaluated on submit. Each rule has `expression` (must return `true` to pass) and `message` (shown on failure). Rules are evaluated in order — first failure wins. Can also be injected at runtime via `fieldLayout[slug].customValidations`. | ### EnumOption | Field | Type | Description | |-------|------|-------------| | `id` | `string` | Option identifier | | `name` | `string` | Display label | | `color` | `string` | Color indicator (hex) | | `icon` | `string` | Docyrus icon name | | `slug` | `string` | Slug value (used by EnumFormField) | | `sort_order` | `number` | Display order | ## Field Action Types These types define the imperative action system used by `useDocyrusFormView` to react to field-value changes. | Type | Description | |------|-------------| | `FieldAction` | A named trigger with `triggerType: 'onFieldChange'` and an ordered list of `FieldActionBlock[]`. | | `FieldActionBlock` | A named block with if / else-if / else branches (`conditionalItems`, `elseActions`) and `unconditionalActions` that always run. | | `FieldActionBlockItem` | One conditional branch: a `condition` (JSONata string or QB rule group) and the `actions` to run when it matches. | | `FieldActionCondition` | Same format as `ComputedBooleanFormula` — JSONata string, QB `RuleGroupType`, or `null` (always-true). | | `FieldActionStep` | Discriminated union of imperative commands: `setFieldValue`, `setFieldValues`, `clearFieldValue`, `showField`, `hideField`, `setFieldRequired`, `setFieldDisabled`, `setFieldReadOnly`. | | `FieldActionPropertyOverrides` | Accumulated per-field overrides produced by action execution: `hidden`, `required`, `disabled`, `readOnly`. |