Component Mapping
Create custom UI components for each field type in your forms.
ComponentMap
The ComponentMap object maps field types to React components:
import type { ComponentMap } from "@fogpipe/forma-react";
const components: ComponentMap = {
text: TextInput,
email: EmailInput,
number: NumberInput,
integer: IntegerInput,
boolean: Checkbox,
select: SelectDropdown,
multiselect: MultiSelect,
date: DatePicker,
textarea: TextArea,
// ... add more as needed
};
Component Props
All components receive { field, spec } props. The field object contains everything needed to render and interact with the field.
Common Field Properties
| Property | Type | Description |
|---|---|---|
name | string | Field path/name |
label | string | Display label |
description | string | undefined | Help text |
placeholder | string | Input placeholder |
required | boolean | Whether field is required |
enabled | boolean | Whether field is editable |
readonly | boolean | Whether field is readonly (visible, value submitted) |
visible | boolean | Whether field is visible |
value | unknown | Current field value |
errors | FieldError[] | Validation errors |
prefix | string | undefined | Prefix adorner text (e.g., "$") — adornable types only |
suffix | string | undefined | Suffix adorner text (e.g., "kg") — adornable types only |
variant | string | undefined | Presentation variant hint (e.g., "slider", "nps") |
variantConfig | Record<string, unknown> | Variant-specific configuration |
Field Methods
| Method | Description |
|---|---|
onChange(value) | Update field value |
onBlur() | Mark field as touched |
Accessibility Attributes
| Property | Description |
|---|---|
aria-invalid | Set when field has errors |
aria-describedby | ID of description/error element |
aria-required | Set when field is required |
Note: These ARIA attributes are available via
getFieldProps()but are not present on the typed component props (TextComponentProps, etc.). Components can derive them fromerrorsandrequired.
Field Type Components
Text Input
import type { TextComponentProps } from "@fogpipe/forma-react";
const TextInput = ({ field }: TextComponentProps) => (
<div>
<label htmlFor={field.name}>
{field.label}
{field.required && <span className="required">*</span>}
</label>
<input
id={field.name}
type="text"
value={field.value || ""}
onChange={(e) => field.onChange(e.target.value)}
onBlur={field.onBlur}
placeholder={field.placeholder}
disabled={!field.enabled}
aria-invalid={field["aria-invalid"]}
aria-describedby={field["aria-describedby"]}
aria-required={field["aria-required"]}
/>
{field.description && (
<p id={`${field.name}-description`}>{field.description}</p>
)}
{field.errors.map((error, i) => (
<p key={i} className="error" role="alert">
{error.message}
</p>
))}
</div>
);
Number Input
import type { NumberComponentProps } from "@fogpipe/forma-react";
const NumberInput = ({ field }: NumberComponentProps) => (
<div>
<label htmlFor={field.name}>{field.label}</label>
<input
id={field.name}
type="number"
value={field.value ?? ""}
onChange={(e) => {
const value = e.target.value === "" ? null : Number(e.target.value);
field.onChange(value);
}}
onBlur={field.onBlur}
disabled={!field.enabled}
/>
</div>
);
Boolean (Checkbox)
import type { BooleanComponentProps } from "@fogpipe/forma-react";
const Checkbox = ({ field }: BooleanComponentProps) => (
<div>
<label>
<input
type="checkbox"
checked={field.value || false}
onChange={(e) => field.onChange(e.target.checked)}
disabled={!field.enabled}
/>
<span>{field.label}</span>
</label>
</div>
);
Select Dropdown
import type { SelectComponentProps } from "@fogpipe/forma-react";
const SelectDropdown = ({ field }: SelectComponentProps) => (
<div>
<label htmlFor={field.name}>{field.label}</label>
<select
id={field.name}
value={field.value || ""}
onChange={(e) => field.onChange(e.target.value)}
onBlur={field.onBlur}
disabled={!field.enabled}
>
<option value="">Select...</option>
{field.options?.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
</div>
);
Multi-Select
import type { MultiSelectComponentProps } from "@fogpipe/forma-react";
const MultiSelect = ({ field }: MultiSelectComponentProps) => {
const values = (field.value as string[]) || [];
const toggleValue = (optionValue: string) => {
const newValues = values.includes(optionValue)
? values.filter((v) => v !== optionValue)
: [...values, optionValue];
field.onChange(newValues);
};
return (
<div>
<label>{field.label}</label>
<div>
{field.options?.map((option) => (
<label key={option.value}>
<input
type="checkbox"
checked={values.includes(String(option.value))}
onChange={() => toggleValue(String(option.value))}
disabled={!field.enabled}
/>
{option.label}
</label>
))}
</div>
</div>
);
};
Date Picker
import type { DateComponentProps } from "@fogpipe/forma-react";
const DatePicker = ({ field }: DateComponentProps) => (
<div>
<label htmlFor={field.name}>{field.label}</label>
<input
id={field.name}
type="date"
value={field.value || ""}
onChange={(e) => field.onChange(e.target.value)}
onBlur={field.onBlur}
disabled={!field.enabled}
/>
</div>
);
Textarea
import type { TextComponentProps } from "@fogpipe/forma-react";
const TextArea = ({ field }: TextComponentProps) => (
<div>
<label htmlFor={field.name}>{field.label}</label>
<textarea
id={field.name}
value={field.value || ""}
onChange={(e) => field.onChange(e.target.value)}
onBlur={field.onBlur}
placeholder={field.placeholder}
disabled={!field.enabled}
rows={4}
/>
</div>
);
Type-Specific Props
Different field types have specific prop types:
| Type | Props Type | Extra Properties |
|---|---|---|
text, email, url | TextComponentProps | - |
number | NumberComponentProps | min, max, step |
integer | IntegerComponentProps | min, max, step |
boolean | BooleanComponentProps | - |
select | SelectComponentProps | options |
multiselect | MultiSelectComponentProps | options |
date | DateComponentProps | - |
datetime | DateTimeComponentProps | - |
array | ArrayComponentProps | itemFields, helpers, minItems, maxItems |
computed | ComputedComponentProps | expression |
display | DisplayComponentProps | content, sourceValue, format |
Options Property
For select and multiselect fields, field.options contains the available choices:
interface SelectOption {
value: string | number;
label: string;
}
Options are pre-filtered by visibleWhen FEEL expressions — your component only receives visible options. See Options Visibility for details.
Styling Best Practices
- Use CSS classes - Apply consistent styling through class names
- Handle all states - Style disabled, error, and focused states
- Show required indicators - Mark required fields clearly
- Display errors prominently - Make validation errors visible
- Support dark mode - Use CSS variables or theme context
Display Field
Display fields show read-only content. They receive DisplayComponentProps instead of the standard field props — notably, they have no value or onChange. See Display Fields for the full guide.
import type { DisplayComponentProps } from "@fogpipe/forma-react";
function DisplayField({ field }: DisplayComponentProps) {
if (!field.visible) return null;
const text =
field.sourceValue != null ? String(field.sourceValue) : field.content;
return (
<div>
{field.label && <p className="font-medium">{field.label}</p>}
{text && <p>{text}</p>}
</div>
);
}
Complete ComponentMap Example
const components: ComponentMap = {
text: TextInput,
email: TextInput,
url: TextInput,
password: PasswordInput,
number: NumberInput,
integer: NumberInput,
boolean: Checkbox,
select: SelectDropdown,
multiselect: MultiSelect,
date: DatePicker,
datetime: DateTimePicker,
textarea: TextArea,
array: ArrayField,
object: ObjectField,
computed: ComputedDisplay,
display: DisplayField,
};