Skip to content
Playground Form Builder

State

export interface FormValidationResult<E> {
formValue: FormValue;
formErrors: FieldErrorsMap<E>;
}
export type FormSubmission<V> = Task<
[event: SubmitEvent],
FormValidationResult<AnyFormValueValidatorError<V>>,
unknown
>;
export type FieldsValidation<V> = Task<
[config: Config, value: FieldValue],
FieldError<AnyFieldValueValidatorError<V>>[],
unknown
>;
export interface FormState<T, V extends Validator>
extends Readonly<Required<IdOptions>> {
readonly submission: FormSubmission<V>;
readonly fieldsValidation: FieldsValidation<V>;
/**
* An accessor that maintains form state consistency:
*
* - A snapshot of the form state is returned on access
* - Default values from JSON Schema are taken into account during assignment
*/
value: T | undefined;
isSubmitted: boolean;
isChanged: boolean;
errors: FieldErrorsMap<PossibleError<V>>;
submit: (e: SubmitEvent) => void;
reset: (e: Event) => void;
/**
* Performs the following actions:
* - Takes a snapshot of the current state
* - Calls the corresponding validator method
* - Groups errors
*
* Actions it does not perform:
* - Updates the form error list
*
* @throws {InvalidValidatorError} If the validator does not have the corresponding method
*/
validate: () => FieldErrorsMap<PossibleError<V>>;
validateAsync: (
signal: AbortSignal
) => Promise<FieldErrorsMap<PossibleError<V>>>;
/** Internals */
[FORM_VALUE]: FormValue;
readonly [FORM_ROOT_ID]: Id;
readonly [FORM_MARK_SCHEMA_CHANGE]: () => void;
readonly [FORM_KEYED_ARRAYS]: KeyedArraysMap;
readonly [FORM_FIELDS_VALIDATION_MODE]: number;
readonly [FORM_SCHEMA]: Schema;
readonly [FORM_UI_SCHEMA_ROOT]: UiSchemaRoot;
readonly [FORM_UI_SCHEMA]: UiSchema;
readonly [FORM_UI_OPTIONS_REGISTRY]: UiOptionsRegistry;
readonly [FORM_UI_EXTRA_OPTIONS]?: ExtraUiOptions;
readonly [FORM_VALIDATOR]: V;
readonly [FORM_MERGER]: FormMerger;
readonly [FORM_ICONS]?: Icons;
readonly [FORM_DISABLED]: boolean;
readonly [FORM_DATA_URL_TO_BLOB]: DataURLToBlob;
readonly [FORM_TRANSLATION]: Translation;
readonly [FORM_TRANSLATE]: Translate;
readonly [FORM_RESOLVER]: ResolveFieldType;
readonly [FORM_THEME]: Theme;
}

If you are using a controlled form, you should consider the following aspects:

It is recommended to initialize the state as follows:

let value = $state(
merger.mergeFormDataAndSchemaDefaults(initialValue, schema)
);

To modify arrays, use one of the following methods:

  1. Reassign
value.array = value.array.concat(123)
  1. Use KeyedArray API
import { createForm, type KeyedArraysMap } from "@sjsf/form";
const keyedArraysMap: KeyedArraysMap = new WeakMap()
let value = $state({ array: [] })
const form = createForm({
keyedArraysMap,
value: [() => value, (v) => (value = v)],
// ...otherOptions
})
const api = keyedArraysMap.get(value.array)
if (api) {
api.push(123)
}

The @sjsf/form package exports a number of utility functions (e.g. getComponent) that let you safely interact with the form’s internal state. These helpers are the preferred way to read or manipulate internals, since they are considered part of the supported API and follow semantic versioning.

For advanced cases, you may also access raw internals by importing symbols from @sjsf/form/internals and using them on a FormState instance.

If you need functionality that isn’t covered by the exported helpers, please open an issue or start a discussion on GitHub. This way we can continue improving the API for everyone.