Skip to content

Standalone Fields

Not every validated input lives inside a <form>. The initField() function lets you enhance individual fields independently — useful for search bars, inline edits, or widget inputs.

<div data-form-field="search"
data-validate='[{"type":"StringLength","options":{"minimum":2,"message":"Enter at least 2 characters"}}]'>
<label for="search">Search</label>
<input id="search" type="text" />
<div id="search-errors" class="invalid-feedback"></div>
</div>
import { initField, registerDefaultValidators } from 'formlayer';
registerDefaultValidators();
const ctrl = initField(document.getElementById('search')!);
ctrl.on('valid', (state) => {
performSearch(state.value);
});
Live Preview

Pass an explicit field class — standalone fields do not use the global field-type registry or data-field-type auto-discovery:

import { ComboboxField } from 'formlayer-plugin-combobox';
const comboCtrl = initField(mySelectWrapper, { field: ComboboxField });

For lazy-loaded custom fields:

const comboCtrl = await initFieldAsync(
mySelectWrapper,
() => import('formlayer-plugin-combobox'),
);

Pass an InitFieldOptions object as the second argument:

const ctrl = initField(myElement, {
validate(value, rules, defaultValidate) {
const result = defaultValidate();
if (result.isValid && value.includes('admin')) {
return { isValid: false, errors: ['Reserved word'] };
}
return result;
},
onServerErrors(errors, fieldName) {
return errors.map(e => `Server: ${e}`);
},
errorsSelector: '[data-field-error]',
renderError: ({ message }) => `<span class="field-error">${message}</span>`,
errorsSeparator: '',
});

For icons, custom container placement, form-level summaries, and the full option reference, see Error Rendering.

initField() returns a FieldController with full control:

const ctrl = initField(myInput);
// Read state
console.log(ctrl.state);
// { name: "search", value: "", isValid: true, isDirty: false, isTouched: false, errors: [] }
// Set value programmatically
ctrl.setValue('hello');
// Trigger validation
const result = ctrl.validate();
console.log(result.isValid, result.errors);
// Listen for changes
ctrl.on('change', (state) => {
console.log('New value:', state.value);
});
// Reset to initial state
ctrl.reset();
// Clean up
ctrl.destroy();

You can pass an <input> directly — initField resolves the closest [data-form-field] wrapper:

const input = document.querySelector('#search input') as HTMLInputElement;
const ctrl = initField(input);

If the input is not inside a [data-form-field] wrapper, initField throws an error.