Skip to main content

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 with the next model and information what { key, value, previousValue } caused the change. previousValue will be undefined if there was no value before.

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.
import { useRef } from 'react';

const formRef = useRef();

const formAction = () => {
// Reset form.
// It will reset changed state, model state in AutoForm, validation
// state in ValidatedForm and rerender.
formRef.reset();

// Trigger form change.
// It's a programmatic equivalent of a change event.
formRef.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.
formRef.submit();
};

<ValidatedForm
onValidate={async (model, error) => {
// You can either ignore validation error...
if (omitValidation(model)) {
return null;
}

// ...or any additional validation if an error is already there...
if (isSomeSpecialCase(error)) {
return MyAPI.checkOtherCondition(model);
}

// ...or feed it with another error.
return MyAPI.validate(model);
}}
validate="onChangeAfterSubmit"
validator={{ clean: true }}
ref={formRef}
/>;

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. You can customize which AutoField should be used with AutoField.componentDetectorContext.

Props:
NameDescription
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 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.
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.
onSubmitSubmit action. When the form is submitted manually or by an HTML5 event, then it's called with the current model. Note: use Promise to return values and errors - synchronous return and throw are disallowed.
readOnlyDefault readOnly prop for all fields. By default it's false - set it to true to make the whole form read-only.
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.
import { useRef } from 'react';

const formRef = useRef();

const formAction = () => {
// Reset form.
// It will reset changed state, model state in AutoForm, validation
// state in ValidatedForm and rerender.
formRef.reset();

// Trigger form change.
// It's a programmatic equivalent of a change event.
formRef.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.
formRef.submit();
};

<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
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)}
onSubmit={model => db.saveThatReturnsPromiseOrNothing(model)}
readOnly={false}
schema={myFormSchema}
showInlineError
ref={formRef}
/>;

Form features

Asynchronous validation

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

The onValidate should return null if the model is valid, otherwise return any error value. The error can be either Promise for asynchronous validation or any other value for synchronous validation (https://github.com/vazco/uniforms/blob/d557f90e6807e34c1ebb9803d44fd799174175f8/packages/uniforms/src/ValidatedForm.tsx#L118-L142).

const MyAPI = {
checkOtherCondition(model): Error | null {
if (model.age < 18) {
return new Error('Too young')
}
return null
}

async validate(model): Error | null {
const result = await fetch('...', { body: JSON.stringify(model) })
const { error } = await result.json()
return error
}
}

const onValidate = async (model, error) => {
// You can either ignore validation error...
if (omitValidation(model)) {
return null;
}

// ...or any additional validation if an error is already there...
if (isSomeSpecialCase(error)) {
return MyAPI.checkOtherCondition(model);
}

// ...or feed it with another error.
return MyAPI.validate(model);
};

// 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:

import { useRef } from 'react';

const MyForm = ({ schema, onSubmit }) => {
const formRef = useRef();

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

You can do the same by using the useForm hook and the formRef property.

function FormControls() {
const { formRef } = useForm();

return (
<>
<button onClick={() => formRef.reset()}>Reset</button>
<button onClick={() => formRef.submit()}>Submit</button>
</>
);
}

function App() {
return (
<AutoForm>
<FormControls />
</AutoForm>
);
}

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.

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}
/>

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}
/>