Form Fields Guide
Learn how to work with different field types including text inputs, checkboxes, dates, and initials.
Field Types Overview
The MIMS SDK supports five field types:
- text - Single or multi-line text input
- signature - Digital signature capture
- checkbox - Boolean check/uncheck field
- date - Date picker field
- initials - Small signature for initials
Text Fields
Text fields are the most common field type for capturing user input:
Text Field Definitiontsx
const textField: TextFieldDefinition = {
id: 'field_name',
type: 'text',
page: 1,
x: 0.15,
y: 0.2,
width: 0.4,
height: 0.04,
required: true,
editable: true,
label: 'Full Name',
placeholder: 'Enter your full legal name',
maxLength: 100,
fontSize: 14,
fontFamily: 'Helvetica',
textAlign: 'left',
multiline: false,
};Text Field Properties
| Prop | Type | Default | Description |
|---|---|---|---|
maxLength | number | — | Maximum character length. |
fontSize | number | 12 | Font size in points. |
fontFamily | string | "Helvetica" | Font family name. |
textAlign | "left" | "center" | "right" | "left" | Text alignment. |
multiline | boolean | false | Allow multiple lines. |
Text Field Component
TextFieldInput.tsxtsx
import { useDocumentStore, type FieldState, type TextFieldValue } from '@mims/sdk-react';
interface TextFieldInputProps {
field: FieldState;
}
export function TextFieldInput({ field }: TextFieldInputProps) {
const setFieldValue = useDocumentStore((s) => s.setFieldValue);
const selectField = useDocumentStore((s) => s.selectField);
const definition = field.definition as TextFieldDefinition;
const value = (field.value as TextFieldValue)?.content || '';
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
let content = e.target.value;
// Enforce max length
if (definition.maxLength && content.length > definition.maxLength) {
content = content.slice(0, definition.maxLength);
}
setFieldValue(field.definition.id, { type: 'text', content });
};
const inputProps = {
value,
onChange: handleChange,
onFocus: () => selectField(field.definition.id),
placeholder: definition.placeholder,
style: {
fontSize: definition.fontSize,
fontFamily: definition.fontFamily,
textAlign: definition.textAlign,
},
className: 'w-full h-full bg-transparent outline-none',
};
return definition.multiline ? (
<textarea {...inputProps} />
) : (
<input type="text" {...inputProps} />
);
}Checkbox Fields
Checkbox fields allow users to agree to terms, select options, or make yes/no choices:
Checkbox Fieldtsx
const checkboxField: CheckboxFieldDefinition = {
id: 'field_agree',
type: 'checkbox',
page: 1,
x: 0.1,
y: 0.85,
width: 0.03,
height: 0.03,
required: true,
editable: true,
label: 'I agree to the terms and conditions',
checkStyle: 'check', // 'check' | 'cross' | 'circle'
};
// Checkbox value
const checkboxValue: CheckboxFieldValue = {
type: 'checkbox',
checked: true,
};Checkbox Properties
| Prop | Type | Default | Description |
|---|---|---|---|
checkStyle | "check" | "cross" | "circle" | "check" | Style of the check mark. |
Checkbox Component
CheckboxFieldInput.tsxtsx
import { useDocumentStore, type FieldState, type CheckboxFieldValue } from '@mims/sdk-react';
interface CheckboxFieldInputProps {
field: FieldState;
}
export function CheckboxFieldInput({ field }: CheckboxFieldInputProps) {
const setFieldValue = useDocumentStore((s) => s.setFieldValue);
const definition = field.definition as CheckboxFieldDefinition;
const checked = (field.value as CheckboxFieldValue)?.checked || false;
const handleToggle = () => {
setFieldValue(field.definition.id, { type: 'checkbox', checked: !checked });
};
const renderCheckMark = () => {
if (!checked) return null;
switch (definition.checkStyle) {
case 'cross':
return <span className="text-lg">×</span>;
case 'circle':
return <span className="w-2 h-2 bg-black rounded-full" />;
default:
return <span className="text-lg">✓</span>;
}
};
return (
<button
onClick={handleToggle}
className={`
w-full h-full border-2 rounded flex items-center justify-center
${checked ? 'border-emerald-500 bg-emerald-50' : 'border-neutral-300'}
`}
>
{renderCheckMark()}
</button>
);
}Date Fields
Date fields provide a date picker for selecting dates:
Date Fieldtsx
const dateField: DateFieldDefinition = {
id: 'field_date',
type: 'date',
page: 1,
x: 0.6,
y: 0.2,
width: 0.25,
height: 0.04,
required: true,
editable: true,
label: 'Date of Birth',
dateFormat: 'MM/DD/YYYY',
minDate: '1900-01-01',
maxDate: new Date().toISOString().split('T')[0], // Today
};
// Date value (always ISO format internally)
const dateValue: DateFieldValue = {
type: 'date',
value: '1990-05-15', // ISO 8601 format
};Date Field Properties
| Prop | Type | Default | Description |
|---|---|---|---|
dateFormat | string | "MM/DD/YYYY" | Display format for the date. |
minDate | string | — | Minimum allowed date (ISO format). |
maxDate | string | — | Maximum allowed date (ISO format). |
Date Field Component
DateFieldInput.tsxtsx
import { useDocumentStore, type FieldState, type DateFieldValue } from '@mims/sdk-react';
interface DateFieldInputProps {
field: FieldState;
}
export function DateFieldInput({ field }: DateFieldInputProps) {
const setFieldValue = useDocumentStore((s) => s.setFieldValue);
const selectField = useDocumentStore((s) => s.selectField);
const definition = field.definition as DateFieldDefinition;
const value = (field.value as DateFieldValue)?.value || '';
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const dateValue = e.target.value;
// Validate against min/max
if (definition.minDate && dateValue < definition.minDate) return;
if (definition.maxDate && dateValue > definition.maxDate) return;
setFieldValue(field.definition.id, { type: 'date', value: dateValue });
};
// Format display value
const formatDate = (isoDate: string) => {
if (!isoDate) return '';
const date = new Date(isoDate);
const format = definition.dateFormat || 'MM/DD/YYYY';
const parts = {
MM: String(date.getMonth() + 1).padStart(2, '0'),
DD: String(date.getDate()).padStart(2, '0'),
YYYY: String(date.getFullYear()),
YY: String(date.getFullYear()).slice(-2),
};
return format.replace(/MM|DD|YYYY|YY/g, (match) => parts[match as keyof typeof parts]);
};
return (
<input
type="date"
value={value}
onChange={handleChange}
onFocus={() => selectField(field.definition.id)}
min={definition.minDate}
max={definition.maxDate}
className="w-full h-full bg-transparent outline-none"
/>
);
}Initials Fields
Initials fields are smaller signature fields for initialing pages or sections:
Initials Fieldtsx
const initialsField: InitialsFieldDefinition = {
id: 'field_initials_1',
type: 'initials',
page: 1,
x: 0.85,
y: 0.95,
width: 0.1,
height: 0.04,
required: true,
editable: true,
label: 'Initials',
};
// Initials value
const initialsValue: InitialsFieldValue = {
type: 'initials',
imageData: 'data:image/png;base64,...', // Or:
typedText: 'JD',
};Dynamic Field Rendering
Create a component that renders the appropriate input based on field type:
FieldRenderer.tsxtsx
import { type FieldState } from '@mims/sdk-react';
interface FieldRendererProps {
field: FieldState;
onOpenSignatureModal: (fieldId: string) => void;
}
export function FieldRenderer({ field, onOpenSignatureModal }: FieldRendererProps) {
switch (field.definition.type) {
case 'text':
return <TextFieldInput field={field} />;
case 'checkbox':
return <CheckboxFieldInput field={field} />;
case 'date':
return <DateFieldInput field={field} />;
case 'signature':
return (
<SignatureFieldOverlay
field={field}
onClick={() => onOpenSignatureModal(field.definition.id)}
/>
);
case 'initials':
return (
<InitialsFieldOverlay
field={field}
onClick={() => onOpenSignatureModal(field.definition.id)}
/>
);
default:
return null;
}
}Field Validation
Implement comprehensive validation for all field types:
Field Validationtsx
import type { FieldState, AnyFieldValue } from '@mims/sdk-react';
export function validateField(field: FieldState): string[] {
const errors: string[] = [];
const { definition, value } = field;
// Required check
if (definition.required && !value) {
errors.push('This field is required');
return errors;
}
if (!value) return errors;
// Type-specific validation
switch (definition.type) {
case 'text': {
const textDef = definition as TextFieldDefinition;
const textVal = value as TextFieldValue;
if (textDef.maxLength && textVal.content.length > textDef.maxLength) {
errors.push(`Maximum ${textDef.maxLength} characters allowed`);
}
break;
}
case 'date': {
const dateDef = definition as DateFieldDefinition;
const dateVal = value as DateFieldValue;
if (dateDef.minDate && dateVal.value < dateDef.minDate) {
errors.push(`Date must be after ${dateDef.minDate}`);
}
if (dateDef.maxDate && dateVal.value > dateDef.maxDate) {
errors.push(`Date must be before ${dateDef.maxDate}`);
}
break;
}
case 'signature':
case 'initials': {
const sigVal = value as SignatureFieldValue | InitialsFieldValue;
if (!sigVal.imageData && !sigVal.typedText) {
errors.push('Please provide a signature');
}
break;
}
}
return errors;
}
// Validate all fields
export function validateAllFields(fields: Map<string, FieldState>): boolean {
let allValid = true;
fields.forEach((field) => {
const errors = validateField(field);
if (errors.length > 0) {
allValid = false;
// Update field errors in store
useDocumentStore.getState().updateFieldDefinition(
field.definition.id,
{ errors } as any
);
}
});
return allValid;
}Field Coordinates
All field coordinates (x, y, width, height) are normalized values between 0 and 1, representing percentages of the page dimensions. This ensures fields render correctly at any zoom level.
Adding Fields Programmatically
Field Templatestsx
import { nanoid } from 'nanoid';
import { useDocumentStore } from '@mims/sdk-react';
// Field templates for common use cases
export const fieldTemplates = {
fullName: (page: number, x: number, y: number): TextFieldDefinition => ({
id: nanoid(),
type: 'text',
page,
x,
y,
width: 0.4,
height: 0.04,
required: true,
editable: true,
label: 'Full Name',
placeholder: 'Enter your full name',
maxLength: 100,
fontSize: 12,
textAlign: 'left',
}),
dateField: (page: number, x: number, y: number): DateFieldDefinition => ({
id: nanoid(),
type: 'date',
page,
x,
y,
width: 0.2,
height: 0.04,
required: true,
editable: true,
label: 'Date',
dateFormat: 'MM/DD/YYYY',
}),
signature: (page: number, x: number, y: number): SignatureFieldDefinition => ({
id: nanoid(),
type: 'signature',
page,
x,
y,
width: 0.35,
height: 0.1,
required: true,
editable: true,
label: 'Signature',
signatureMode: 'draw',
}),
checkbox: (page: number, x: number, y: number, label: string): CheckboxFieldDefinition => ({
id: nanoid(),
type: 'checkbox',
page,
x,
y,
width: 0.025,
height: 0.025,
required: false,
editable: true,
label,
checkStyle: 'check',
}),
};
// Usage
function AddFieldsToolbar() {
const currentPage = useDocumentStore((s) => s.currentPage);
const addField = useDocumentStore((s) => s.addField);
return (
<div className="flex gap-2">
<button onClick={() => addField(fieldTemplates.fullName(currentPage, 0.1, 0.1))}>
+ Name Field
</button>
<button onClick={() => addField(fieldTemplates.dateField(currentPage, 0.1, 0.2))}>
+ Date Field
</button>
<button onClick={() => addField(fieldTemplates.signature(currentPage, 0.1, 0.8))}>
+ Signature
</button>
</div>
);
}