Form Fields
Dynamic form field system powered by TanStack Form. 49 field types with automatic dispatch via DynamicFormField.
Installation
pnpm dlx @docyrus/cli add @docyrus/ui-form-fieldsnpx shadcn@latest add avatar badge button calendar checkbox command dialog field input label phone-input popover scroll-area select separator sortable switch tabs textarea time-picker tooltipnpx shadcn@latest add @plate/ai-kit @plate/ai-toolbar-button @plate/align-kit @plate/align-toolbar-button @plate/autoformat-kit @plate/basic-blocks-kit @plate/basic-marks-kit @plate/block-menu-kit @plate/block-placeholder-kit @plate/callout-kit @plate/code-block-kit @plate/column-kit @plate/comment-kit @plate/comment-toolbar-button @plate/date-kit @plate/discussion-kit @plate/dnd-kit @plate/editor @plate/editor-base-kit @plate/editor-static @plate/emoji-kit @plate/emoji-toolbar-button @plate/equation-toolbar-button @plate/exit-break-kit @plate/fixed-toolbar @plate/floating-toolbar @plate/font-color-toolbar-button @plate/font-kit @plate/font-size-toolbar-button @plate/history-toolbar-button @plate/indent-toolbar-button @plate/insert-toolbar-button @plate/line-height-kit @plate/line-height-toolbar-button @plate/link-kit @plate/link-toolbar-button @plate/list-kit @plate/list-toolbar-button @plate/mark-toolbar-button @plate/math-kit @plate/mention-kit @plate/mode-toolbar-button @plate/more-toolbar-button @plate/slash-kit @plate/suggestion-kit @plate/suggestion-toolbar-button @plate/table-kit @plate/table-toolbar-button @plate/toc-kit @plate/toggle-kit @plate/toggle-toolbar-button @plate/toolbar @plate/turn-into-toolbar-buttonpnpm add @tanstack/react-form date-fns react-day-picker platejs react-email-editor react-querybuilder @uiw/react-codemirror @uiw/codemirror-extensions-langsUsage
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 (
<form onSubmit={(e) => { e.preventDefault(); form.handleSubmit(); }}>
{fields.map((field) => (
<DynamicFormField
key={field.id}
field={field}
form={form}
enumOptions={field.type === 'field-select' ? enumOptions : undefined}
/>
))}
</form>
);
}Individual Field Import
You can import individual field components to reduce bundle size:
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 |
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
LocationSelectFormFieldautomatically upgrades to the map-based picker in Vite apps whenVITE_GOOGLE_MAPS_API_KEYis 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 — 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 — drag-and-drop tree + JSON tab |
field-jsonata | JsonataFormField | Dialog-based 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.
<DynamicFormField
field={fieldConfig}
form={form}
enumOptions={options}
required
disabled={false}
className="my-field"
/>DocEditorFormField
Rich text editor with preset-based plugin and toolbar system.
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';
<DocEditorFormField field={fieldConfig} form={form} preset="rich" />| 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.
import { HtmlEditorFormField } from '@docyrus/ui/components/form-fields/html-editor-form-field';
<HtmlEditorFormField field={fieldConfig} form={form} preset="rich" />| 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.
import { CodeEditorFormField } from '@docyrus/ui/components/form-fields/code-editor-form-field';
<CodeEditorFormField field={fieldConfig} form={form} language="python" />| 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.
Schema mode — pass schema prop with IField[] to render sub-fields per row:
import { SchemaRepeaterFormField } from '@docyrus/ui/components/form-fields/schema-repeater-form-field';
<SchemaRepeaterFormField
field={fieldConfig}
form={form}
schema={[
{ id: '1', name: 'Name', slug: 'name', type: 'field-text' },
{ id: '2', name: 'Role', slug: 'role', type: 'field-select' }
]}
schemaEnumOptions={{ role: [{ id: 'admin', name: 'Admin' }, { id: 'editor', name: 'Editor' }] }}
/>Key-value mode — omit schema prop for simple key-value pairs:
<SchemaRepeaterFormField field={fieldConfig} form={form} />| 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<string, EnumOption[]> | — | 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.
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' }
];
<FieldMappingFormField
field={fieldConfig}
form={form}
availableFields={availableFields}>
<TextFormField field={fieldConfig} form={form} />
</FieldMappingFormField>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, <br>, 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.
import { CheckboxGroupFormField } from '@docyrus/ui/components/form-fields/checkbox-group-form-field';
<CheckboxGroupFormField
field={fieldConfig}
form={form}
enumOptions={DEPARTMENT_OPTIONS}
variant="card"
columnCount={2}
/>| 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. |