Forms

Forms components

Most of the time you'll be using either AutoForm or ValidatedForm, but there are also other form components (rather low-level ones) with different capabilities.

ComponentSelf-generated?Self-managed?Self-validated?
AutoForm✔️✔️✔️
ValidatedQuickForm✔️✖️✔️
ValidatedForm✖️✖️✔️
QuickForm✔️✖️✖️
BaseForm✖️✖️✖️

AutoForm

AutoForm extends ValidatedQuickForm with state management. It is the most user-friendly and commonly used form. It's self-generated so if you provide a schema, the fields will be automatically rendered. These fields will be also validated. By default, the validation will take place onSubmit, and onChange after the first submission.

Props:
NameDescription
onChangeModelLike onChange but for the whole model. Triggered just after onChange but with the next model instead of (key, value) pair.

Note: All ValidatedQuickForm props are also accepted and all methods are available. In other words, that means that AutoForm receives all props listed on this page.

Props usage:
import { AutoForm } from 'uniforms'; // Or from the theme package.
<AutoForm onChangeModel={model => console.log(model)} />;

ValidatedQuickForm

This form combines both QuickForm and ValidatedForm features. It is not self-managed, however, it will automatically generate fields based on the provided schema and validate them.

Note: All QuickForm props are also accepted and all methods are available.
Note: All ValidatedForm props are also accepted and all methods are available.

ValidatedForm

(It's rather an internal form, but it's still exported.)

It's based on BaseForm and extends its functionality by enabling automatic form validation. Its purpose is providing validation functions. It's not autogenerated, so if you want to see any fields rendered, you have to manually add them. ValidatedForm is not self-managed, so you won't be able to type anything until there is no onChange handler, however, there will be validation checks.

Props:
NameDescription
onValidateAdditional asynchronous validation. Schema validation has to be sync, so this is the only way to achieve async validation.
validateValidation mode. By default, the form will start to validate from the time of the first submit and then revalidate on every change. It's onChangeAfterSubmit. There are also onChange and onSubmit modes, but those are quite self-explanatory.
validatorValidator options. It's passed to getValidator of your schema bridge. It really depends on your schema.

Note: All BaseForm props are also accepted and all methods are available.

Props usage:
import { ValidatedForm } from 'uniforms'; // Or from the theme package.
<ValidatedForm
onValidate={(model, error, callback) => {
// You can either ignore validation error...
if (omitValidation(model)) {
return callback(null);
}
// ...or any additional validation if an error is already there...
if (error) {
return callback();
}
// ...or feed it with another error.
MyAPI.validate(model, error => callback(error || null));
}}
validate="onChangeAfterSubmit"
validator={{ clean: true }}
ref={form => {
// Validate form with the current model.
// Returns a Promise, which rejects with an validation error or
// resolves without any value. Note, that it resolves/rejects AFTER
// the component is rerendered.
form.validate();
// Validate form with key set to value.
// You can use it to check, if a given value will pass the
// validation or not. Returns validation Promise, as described above.
form.validate(key, value);
// Validate form with the given model.
// Rather internal function. Returns validation Promise, as described
// above.
form.validateModel(model);
}}
/>;

QuickForm

(It's rather an internal form, but it's still exported.)

It's based on BaseForm and extends its functionality by enabling automatic form generation. If you provide a schema, the fields will be automatically rendered. However, QuickForm is not self-managed, so you won't be able to type anything until there is no onChange handler.

Props:
NameDescription
autoFieldCustom AutoField. It should be anything that will pass through React.createElement.
errorsFieldCustom ErrorsField. It should be anything that will pass through React.createElement.
submitFieldCustom SubmitField. It should be anything that will pass through React.createElement.

Note: All BaseForm props are also accepted and all methods are available.

Props usage:
import { QuickForm } from 'uniforms'; // Or from the theme package.
<QuickForm
autoField={CustomAutoField}
errorsField={CustomErrorsField}
submitField={CustomSubmitField}
/>;

BaseForm

(It's rather an internal form, but it's still exported.)

It's the very basic form & foundation for the other forms. It's not autogenerated, so if you want to see any fields rendered, you have to manually add them. However, BaseForm is not self-managed, so you won't be able to type anything until there is no onChange handler.

Props:
NameDescription
autosaveDelayAutosave delay. Set 0 for an instant autosave.
autosaveEnable autosave. Every change triggers onSubmit.
disabledDefault disabled prop for all fields. By default it's false - set it to true to disable the whole form.
errorValidation error. Current validation state. It should be either compatible with your schema or an Error object.
gridBootstrap grid layout style. Passing a number is an equivalent of {sm: n}. Object is a {mode: size} object. Complete string is simply passed through. Available in: bootstrap3, bootstrap4.
labelDefault label prop for all fields. By default it's true - set it to false to disable labels for the whole form.
modelForm model. An object with {field: value} structure. It doesn't matter if it has a prototype or not, but keep in mind that in onSubmit or in onChangeModel you'll receive a plain object. If you treat form as an input, then this is a value.
modelTransformModel transform. Function transforming one model into another. It's used in a few situations (modes) described below. Do not mutate a given model!
onChangeField change action. It receives two arguments: key and value, where the key is a dot-separated path to the changed field and value is a requested value.
onSubmitFailureSubmit failure action. If onSubmit returns a Promise, then this will be attached to its .catch chain.
onSubmitSuccessSubmit success action. If onSubmit returns a Promise, then this will be attached to its .then chain.
onSubmitSubmit action. When the form is submitted manually or by an HTML5 event, then it's called with the current model.
placeholderDefault placeholder prop for all fields. By default it's false - set it to true to enable placeholders for the whole form.
schemaForm schema. It's used for form generation in QuickForm and validation in ValidatedForm.
showInlineErrorDefault showInlineError prop for all fields. By default it's false - set it to true to enable inline errors for the whole form. Available in: antd, bootstrap3, bootstrap4, semantic.
Props usage:
import { BaseForm } from 'uniforms'; // Or from the theme package.
<BaseForm
autosaveDelay={0}
autosave={false}
disabled={false}
error={new Error('Nope.')}
grid={3} // 'col-3-sm' on label, 'col-9-sm' on input
grid="4" // 'col-4-sm' on label, 'col-8-sm' on input
grid={{ md: 5 }} // 'col-5-md' on label, 'col-7-md' on input
grid="col-6-xl" // 'col-6-xl' on label, 'col-6-xl' on input
label
model={{ fieldA: 1 }}
modelTransform={(mode, model) => {
// This model will be passed to the fields.
if (mode === 'form') {
/* ... */
}
// This model will be submitted.
if (mode === 'submit') {
/* ... */
}
// This model will be validated.
if (mode === 'validate') {
/* ... */
}
// Otherwise, return unaltered model.
return model;
}}
onChange={(key, value) => console.log(key, value)}
onSubmitFailure={() => alert('Promise rejected!')}
onSubmitSuccess={() => alert('Promise resolved!')}
onSubmit={model => db.saveThatReturnsPromiseOrNothing(model)}
placeholder={false}
schema={myFormSchema}
showInlineError
ref={form => {
// Reset form.
// It will reset changed state, model state in AutoForm, validation
// state in ValidatedForm and rerender.
form.reset();
// Trigger form change.
// It's a programmatic equivalent of a change event.
form.change(key, value);
// Submit form.
// It's a programmatic equivalent of a submit event. Returns a promise,
// which will either resolve with submitted form or reject with
// validation error in ValidatedForm. You can also use onSubmitFailure
// and onSubmitSuccess instead of doing form.submit().then().
form.submit();
}}
/>;

Form features

Asynchronous validation

AutoForm and ValidatedForm both accept an onValidate prop. It can be used to create an asynchronous validation:

const onValidate = (model, error, callback) => {
// You can either ignore validation error...
if (omitValidation(model)) {
return callback(null);
}
// ...or any additional validation if an error is already there...
if (error) {
return callback();
}
// ...or feed it with another error.
MyAPI.validate(model, error => callback(error || null));
};
// Later...
<ValidatedForm {...props} onValidate={onValidate} />;

Autosave

Every form has autosave functionality. If you set an autosave prop, then every change will trigger a submit. There's also an autosaveDelay prop - a minimum time between saves in milliseconds (default: 0).

Example:

<AutoForm
autosave
autosaveDelay={5000} // 5 seconds
schema={schema}
onSubmit={onSubmit}
/>

Methods

You can use React ref prop to manually access form methods. Example usage:

const MyForm = ({ schema, onSubmit }) => {
let formRef;
return (
<section>
<AutoForm
ref={ref => (formRef = ref)}
schema={schema}
onSubmit={onSubmit}
/>
<small onClick={() => formRef.reset()}>Reset</small>
<small onClick={() => formRef.submit()}>Submit</small>
</section>
);
};

All available methods:

change(key, value)

Triggers a form change. It's a programmatic equivalent of a change event.

reset()

Resets a form. It will also reset changed state, model state (only in AutoForm), validation state (only in ValidatedForm) and trigger a rerender.

submit()

Submits a form. It's a programmatic equivalent of a submit event. Returns a promise, which will either resolve with a submitted model or reject with validation error in ValidatedForm. You can also use onSubmitFailure and onSubmitSuccess instead of doing form.submit().then().

validate()

(added in ValidatedForm)

Validates a form with the current model. Returns a Promise, which rejects with a validation error or resolves without any value. Note, that it resolves/rejects after the component is rerendered.

validate(key, value)

(added in ValidatedForm)

Validates a form with key set to value. You can use it to check, if a given value will pass the validation or not. Returns validation Promise, as described above.

validateModel(model)

(added in ValidatedForm)

Validates a form with the given model. Returns validation Promise, as described above.

Change reactions

If you want to make one field to influence others, simply extend AutoForm and override onChange method.

Example:

class ChainForm extends AutoForm {
onChange(key, value) {
if (key === 'key_to_intercept') return;
if (key === 'key_to_translate') return super.onChange('another_key', value);
if (key === 'key_to_mutate') {
super.onChange('another_key1', value * 2);
super.onChange('another_key2', value / 2);
return;
}
super.onChange(key, value);
}
}

It can be easily applied multiple times to make your forms even more reusable.

Example:

const withMultipliedField = (fieldA, fieldB, Form) =>
class withMultipliedFieldForm extends Form {
onChange(key, value) {
// Multiply fieldA
if (key === fieldA) super.onChange(fieldB, value + value);
// Pass every change
super.onChange(key, value);
}
};

Model transformations

If you need to transform model before it will be validated, submitted or passed down to the fields, there's a modelTransform prop, which should be used in those use cases.

Example:

<AutoForm
// Do not mutate given model!
modelTransform={(mode, model) => {
// This model will be passed to the fields.
if (mode === 'form') {
/* ... */
}
// This model will be submitted.
if (mode === 'submit') {
/* ... */
}
// This model will be validated.
if (mode === 'validate') {
/* ... */
}
// Otherwise, return unaltered model.
return model;
}}
onSubmit={onSubmit}
schema={schema}
/>

Post-submit handling

It's a good UX practice to tell your users that something failed or succeed. To make it simpler, there are onSubmitFailure and onSubmitSuccess props.

Example:

<AutoForm
schema={schema}
onSubmit={doc => db.saveThatReturnsPromise(doc)}
onSubmitSuccess={() => alert('Promise resolved!')}
onSubmitFailure={() => alert('Promise rejected!')}
/>

Validation options and modes

Any form can be validated in one those three styles:

  • onChange Validate on every change.

  • onChangeAfterSubmit (default) Validate on every change, but only after first submit.

  • onSubmit Validate on every submit.

If your schema validator accepts any options, those can be passed in validator prop.

Example:

<AutoForm
validate="onChange"
validator={validatorOptions}
schema={schema}
onSubmit={onSubmit}
/>