6.x Upgrade Guide
New packages
There are 5 new packages added in RJSF version 6:
@rjsf/daisyui: This is new theme based on thedaisyuitoolkit@rjsf/mantine: This is new theme based on themantinetoolkit@rjsf/primereact: This is a new theme based on thePrimeReacttoolkit@rjsf/react-bootstrap: This is rename of thebootstrap-4theme with an upgrade to the latest version ofreact-bootstrap@rjsf/shadcn: This is new theme based on theshadcntoolkit
New deprecations
UiSchema.rootFieldId
The ui:rootFieldId notation on UiSchema was deprecated in favor of Form.idPrefix.
This notation will be removed in a future major version release
@rjsf/semantic-ui
The semantic-ui theme is deprecated and will be removed in a future major version release unless sematic-ui-react version 3 is released with React 19 support.
Although that seems unlikely as no changes have been made to version 3 since December 2023.
Breaking changes
CJS build changes
Due to making all of the package.json files for the @rjsf/* libraries include "type": "module" to better support modern ESM builds, the generation of the Common JS distributions were updated to produce .cjs files rather than .js files.
Hopefully this will not cause any issues with existing uses of the libraries. If so, one may need to switch from doing an import of the CJS build to doing a require().
Theme removals
The following themes were removed due to duplication of a framework with a newer theme
bootstrap-4
The older bootstrap-4 theme has been removed in favor of the react-bootstrap theme
fluent-ui
The older fluent-ui theme has been removed in favor of the fluentui-rc theme
material-ui
The older material-ui theme has been removed in favor of the mui theme
validator-ajv6
This deprecated validator has been removed. Use the validator-ajv8.
Theme version support changes
@rjsf/antd
Version 6 is dropping official support for antd version 4. You must upgrade to version 5.
@rjsf/chakra-ui
Version 6 is dropping official support for @chakra-ui version 2. You must upgrade to version 3. We are also requiring
chakra-react-select version >=6 as a result.
@mui version 5 and 6
Version 6 is dropping official support for @mui/material and @mui/icons-material versions 5 and 6 due to the adoption
of breaking changes in version 7. You must upgrade to version 7.
@rjsf/semantic-ui
Version 6 is dropping official support for semantic-ui-react version 1. You must upgrade to version 2.
Node support
Version 6 is dropping official support for Node 14, 16, and 18 as they are no longer maintained versions.
Please use Node 22 when making any changes to package.json and package-lock.json files.
All PR and branch builds are running against Node 20 and 22.
React version
RJSF is no longer actively supporting React version < 18.x. React 18 is officially supported on all the themes.
React 19 support is expected before the end of beta (although several developers have already upgraded with no problems).
Form POTENTIAL BREAKING CHANGES
The onChange() and onSubmit() callbacks were accidentally returning extra data along with what was listed in the IChangeEvent interface.
In v6, this was fixed so that only the data described by the IChangeEvent interface is returned.
If you were relying on that extra data in the callbacks, we are sorry for the inconvenience.
A bug was fixed such that live validation will no longer be triggered on the data provided during the initial rendering of the Form.
If you require that your initial data have errors shown, then you can programmatically validate it after the initial render.
IdSchema replacement BREAKING CHANGE
The recursive IdSchema type and all its usages was replaced with a much simpler FieldPathId type.
This was done to remove a performance headache associated with having to recursively generate the IdSchema for every change made to formData.
Moreover, after some evaluation, 99% of the use cases for the IdSchema were simply to get the id for a field.
FieldPathId is defined as:
/** The list of path elements that represents where in the schema a field is located. When the item in the field list is
* a string, then it represents the name of the property within an object. When it is a number, then it represents the
* index within an array.
*
* For example:
* `[]` represents the root object of the schema
* `['foo', 'bar']` represents the `bar` element contained within the `foo` element of the schema
* `['baz', 1]` represents the second element in the list `baz` of the schema
*/
export type FieldPathList = (string | number)[];
/** Type describing an id and path used for a field */
export type FieldPathId = {
/** The id for a field */
$id: string;
/** The path for a field */
path: FieldPathList;
};
idSchema props
In addition, everywhere the idSchema: IdSchema<T> prop was part of a RJSF type, it was replaced with fieldPathId: FieldPathId.
The affected types are:
- FieldErrorProps
- FieldHelpProps
- FieldProps
- UnsupportedFieldProps
- ArrayFieldTitleProps
- ArrayFieldDescriptionProps
- ArrayFieldItemButtonsTemplateProps
- ArrayFieldTemplateProps
- ObjectFieldTemplateProps
If you have built a custom component using one of those types that used idSchema: IdSchema, simply replace that type and property name with fieldPathId: FieldPathId.
For example, if you had a custom field:
import { FieldProps } from '@rjsf/utils';
// Version 5-based custom widget
function CustomField(props: FieldProps) {
const {
idSchema: { $id },
formData,
value,
} = props;
return <div id={$id}>// Your field implementation</div>;
}
// Version 6-based custom widget
function CustomField(props: FieldProps) {
const {
fieldPathId: { $id },
formData,
value,
} = props;
return <div id={$id}>// Your field implementation</div>;
}
toIdSchema() removed
Finally, the toIdSchema() utility function was deleted and replaced with a new toFieldPathId() function documented here
formContext BREAKING CHANGES
Removed the unnecessary formContext property from the following interfaces since it is readily available in the registry.
If you were using the formContext in your custom template, field or widget, simply get it from the registry instead.
Interfaces with formContext removed:
ErrorListProps- The properties that are passed to anErrorListTemplateimplementationFieldProps- The properties that are passed to aFieldimplementationFieldTemplateProps- The properties that are passed to aFieldTemplateimplementationArrayFieldTemplateProps- The properties that are passed to anArrayFieldTemplateimplementationWidgetProps- The properties that are passed to aWidgetimplementation
Fields BREAKING CHANGES
FieldProps.onChange
The onChange handling for fields has been changed to fix a serious bug related to nearly simultaneous updates losing data.
Previously in 5.x, data change handling worked by passing a complete newFormData object up to the Form from the underlying Fields.
In 6.x, data change handling now works by passing just the changed newValue for a Field and the path array of the Field within the formData, with the Form itself being responsible for injecting the changed data into the formData.
As a result, the FieldProps interface was updated with the following breaking change so that custom Field authors are forced to respond to this update:
// Version 5's `onChange` handler:
/** The field change event handler; called with the updated form data and an optional `ErrorSchema` */
onChange: (newFormData: T | undefined, es?: ErrorSchema<T>, id?: string) => any;
// Version 6's `onChange` handler:
/** The field change event handler; called with the updated field value, the optional change path for the value
* (defaults to an empty array), an optional ErrorSchema and the optional id of the field being changed
*/
onChange: (newValue: T | undefined, path: FieldPathList, es?: ErrorSchema<T>, id?: string) => void;
In order to support letting the Form know what the path of the change was, a new path: FieldPathList parameter was injected into the handler before the es?: ErrorSchema<T> parameter.
If you have written a custom Field that implements merging the new value into the newFormData, now you just need to pass that value and provide the fieldPathId.path to the onChange function.
Here is an example of a custom Field that was updated due to this change:
import { FieldProps } from '@rjsf/utils';
import { getDefaultRegistry } from '@rjsf/core';
const { ArrayField } = getDefaultRegistry().fields;
// Version 5-based custom field
function CustomField(props: FieldProps) {
const {
idSchema: { $id },
formData,
onChange,
} = props;
const changeHandlerFactory = (fieldName: string) => (event: any) => {
onChange({ ...formData, [fieldName]: event.target.value });
};
return (
<>
<h4>Location</h4>
<div>
<label htmlFor={`${$id}-city`}>City</label>
<input
className='form-control'
id={`${$id}-city`}
required={false}
placeholder=''
type='text'
value={formData?.city || ''}
onChange={changeHandlerFactory('city')}
/>
</div>
<div>
<label htmlFor={`${$id}-lat`}>Latitude</label>
<input
className='form-control'
id={`${$id}-lat`}
type='number'
value={formData?.lat || 0}
onChange={changeHandlerFactory('lat')}
/>
</div>
<div>
<label htmlFor={`${$id}-lon`}>Longitude</label>
<input
className='form-control'
id={`${$id}-lon`}
type='number'
value={formData?.lon || 0}
onChange={changeHandlerFactory('lon')}
/>
</div>
</>
);
}
// Version 6-based custom field
function CustomField(props: FieldProps) {
const {
fieldPathId: { $id, path },
formData,
onChange,
} = props;
const changeHandlerFactory = (fieldName: string) => (event: any) => {
onChange(event.target.value, [...path, fieldName]);
};
return (
<>
<h4>Location</h4>
<div>
<label htmlFor={`${$id}-city`}>City</label>
<input
className='form-control'
id={`${$id}-city`}
required={false}
placeholder=''
type='text'
value={formData?.city || ''}
onChange={changeHandlerFactory('city')}
/>
</div>
<div>
<label htmlFor={`${$id}-lat`}>Latitude</label>
<input
className='form-control'
id={`${$id}-lat`}
type='number'
value={formData?.lat || 0}
onChange={changeHandlerFactory('lat')}
/>
</div>
<div>
<label htmlFor={`${$id}-lon`}>Longitude</label>
<input
className='form-control'
id={`${$id}-lon`}
type='number'
value={formData?.lon || 0}
onChange={changeHandlerFactory('lon')}
/>
</div>
</>
);
}
The same change also applies to the ErrorSchema object being passed to the Form.
Therefore, if your custom Field also updated the ErrorSchema to add a new error, now you just need to pass that error as well.
Finally, the errors are preserved across validations, so if you want to clear an error you passed via onChange, you will have to pass undefined
Here is an example of a custom Field that was updated due to this change:
import { FieldProps } from '@rjsf/utils';
import { getDefaultRegistry } from '@rjsf/core';
const { StringField } = getDefaultRegistry().fields;
// Version 5-based custom field
function StringFieldError(props: FieldProps) {
const onChange = (newFormData: any | undefined, es?: ErrorSchema, id?: string) => {
let raiseError = es;
if (newFormData === 'test') {
raiseError = {
...es,
__errors: ['Value cannot be "test"'],
};
}
props.onChange(newFormData, raiseError, id);
};
return <StringField {...props} onChange={onChange} />;
}
// Version 6-based custom field
function StringFieldError(props: FieldProps) {
const onChange = (newValue: any | undefined, path: FieldPathList, es?: ErrorSchema, id?: string) => {
let raiseError = es;
if (newValue === 'test') {
raiseError = {
__errors: ['Value cannot be "test"'],
};
}
props.onChange(newValue, path, raiseError, id);
};
return <StringField {...props} onChange={onChange} />;
}
FieldProps.idPrefix and FieldProps.idSeparator refactor
The idPrefix and idSeparator props were refactored from the FieldProps interface to the new GlobalFormOptions interface,
If your custom implementation required either of those props, you can simply obtain them from the registry.globalFormOptions variable instead.
FieldTemplateProps and WrapIfAdditionalTemplateProps BREAKING CHANGES
During the conversion of ObjectField to a stateless functional component, the additionalProperties-based props in these two interfaces were simplified by replacing two callback-generators with memoized callbacks.
If you have your own implementation of the FieldTemplate and/or WrapIfAdditionalTemplate template(s), you will have to rename the props mentioned below with their replacements.
onKeyChange replacement
The onKeyChange: (value: string) => () => void callback-generator function was replaced with the following 2 memoized callbacks, (one is a helper callback, since most of the key renames were triggered off of an input blur):
/** Callback used to handle the changing of an additional property key's name with the new value
*/
onKeyRename: (newKey: string) => void;
/** Callback used to handle the changing of an additional property key's name when the input is blurred. The event's
* target's value will be used as the new value. Its a wrapper callback around `onKeyRename`
*/
onKeyRenameBlur: (event: FocusEvent<HTMLInputElement>) => void;
onDropPropertyClick replacement
The onDropPropertyClick: (value: string) => () => void callback-generator function was replaced with the following memoized callback:
/** Callback used to handle the removal of the additionalProperty */
onRemoveProperty: () => void;
ObjectFieldTemplateProps BREAKING CHANGES
During the conversion of ObjectField to a stateless functional component, the additionalProperties-based prop in this interface was simplified by replacing a callback-generator with a memoized callback.
If you have your own implementation of the ObjectFieldTemplate template, you will have to rename the prop mentioned below with its replacement.
onAddClick replacement
The onAddClick: (schema: S) => () => void callback-generator function was replaced with the following memoized callback:
/** Callback to use in order to add an new additionalProperty to the object field (to be used with
* additionalProperties and patternProperties)
*/
onAddProperty: () => void;
Registry BREAKING CHANGES
A new required prop, globalFormOptions: GlobalFormOptions, was added to the Registry interface.
This could affect your tests if you have your own way of creating registry objects to test your custom widgets, fields and/or templates.
If you are creating Registry objects, you simply need to add an empty object to the globalFormOptions prop.
You may also, provide values for any of the optional props contained within the GlobalFormOptions type.
The definition of the GlobalFormOptions object is:
/** The set of options from the `Form` that will be available on the `Registry` for use in everywhere the `registry` is
* available.
*/
export type GlobalFormOptions = {
/** To avoid collisions with existing ids in the DOM, it is possible to change the prefix used for ids;
* Default is `root`. This prop is passed to the `toFieldPathId()` function within the RJSF field implementations.
*/
readonly idPrefix?: string;
/** To avoid using a path separator that is present in field names, it is possible to change the separator used for
* ids; Default is `_`. This prop is passed to the `toFieldPathId()` function within the RJSF field implementations.
*/
readonly idSeparator?: string;
/** The component update strategy used by the Form and its fields for performance optimization */
readonly experimental_componentUpdateStrategy?: 'customDeep' | 'shallow' | 'always';
/** Optional function to generate custom HTML name attributes for form elements. Receives the field path segments
* and element type (object or array), and returns a custom name string. This allows backends like PHP/Rails
* (`root[tasks][0][title]`) or Django (`root__tasks-0__title`) to receive form data in their expected format.
*/
readonly nameGenerator?: NameGeneratorFunction;
};
To help support this change for test writers, a new getTestRegistry() function has been provided in @rjsf/core.
Templates BREAKING CHANGES
ArrayFieldTemplateItemType renamed
The type ArrayFieldTemplateItemType was renamed to ArrayFieldItemTemplateProps type, which matches the template properties naming pattern.
Also changed the key: string prop to itemKey: string to avoid a name collision with React
ArrayFieldItemTemplate BREAKING CHANGES
The ArrayFieldItemTemplateProps was refactored to extract the following props into out a new ArrayFieldItemButtonsTemplateProps:
canAdd: A boolean value stating whether new items can be added to the array.hasCopy: A boolean value stating whether the array item can be copied.hasMoveDown: A boolean value stating whether the array item can be moved down.hasMoveUp: A boolean value stating whether the array item can be moved up.hasRemove: A boolean value stating whether the array item can be removed.onAddIndexClick: (index) => (event?) => void: Returns a function that adds a new item atindex.onCopyIndexClick: (index: number) => (event?: any) => void;: Returns a function that copies the item atindexinto the position atindex + 1.onDropIndexClick: (index) => (event?) => void: Returns a function that removes the item atindex.onReorderClick: (index, newIndex) => (event?) => void: Returns a function that swaps the items atindexwithnewIndex.
In addition to this refactor, the conversion of the ArrayField template to functional components caused the replacement of the 4 callback-generator functions with 5 memoizable functions.
onAddIndexClick replacement
The onAddIndexClick: (index) => (event?) => void callback-generator function was replaced with the following memoized callback:
/** Callback function that adds a new item below this item */
onAddItem: (event?: any) => void;
onCopyIndexClick replacement
The onCopyIndexClick: (index: number) => (event?: any) => void; callback-generator function was replaced with a simpler, memoized callback:
/** Callback function that copies this item below itself */
onCopyItem: (event?: any) => void;
onDropIndexClick replacement
The onDropIndexClick: (index) => (event?) => void callback-generator function was replaced with the following memoized callback:
/** Callback function that removes the item from the list */
onRemoveItem: (event?: any) => void;
onReorderClick replacement
The onReorderClick: (index, newIndex) => (event?) => void callback-generator function was replaced with the following 2 memoized callbacks:
/** Callback function that moves the item up one spot in the list */
onMoveUpItem: (event?: any) => void;
/** Callback function that moves the item down one spot in the list */
onMoveDownItem: (event?: any) => void;
A new buttonsProps prop was added of the type ArrayFieldItemButtonsTemplateProps
This new type was then used to create a new ArrayFieldItemButtonsTemplate in the Registry.templates.
See ArrayFieldItemButtonTemplate
If you have implemented your own ArrayFieldItemTemplate or ArrayField then you will have to account for these changes.
ArrayFieldTemplate BREAKING CHANGE
In order to support the conversion of ArrayField to functional components, the items property was changed from an
array of ArrayFieldItemTemplateProps information to an array of React elements. These elements are result of the ArrayField
actually rendering the ArrayFieldItemTemplate directly, rather than deferring it the ArrayFieldTemplate.
If you previously provided a custom ArrayFieldTemplate to do the full rendering of your arrays, now you will have to
separate the logic of generating the items into your own custom ArrayFieldItemTemplate. For example:
// v5 ArrayFieldTemplate
import { ArrayFieldTemplateItemType, ArrayFieldTemplateProps } from '@rjsf/utils';
function CustomArrayFieldTemplate(props: ArrayFieldTemplateProps) {
const { items, canAdd, onAddClick } = props;
return (
<div className='array'>
{items &&
items.map((element: ArrayFieldTemplateItemType) => (
<div key={element.key} className='array-item' data-rjsf-itemkey={element.key}>
<div>{element.children}</div>
{(element.hasMoveUp || element.hasMoveDown) && (
<button
className='array-item-move-down'
onClick={element.onReorderClick(element.index, element.index + 1)}
>
Down
</button>
)}
{(element.hasMoveUp || element.hasMoveDown) && (
<button className='array-item-move-up' onClick={element.onReorderClick(element.index, element.index - 1)}>
Up
</button>
)}
{element.hasCopy && (
<button className='array-item-copy' onClick={element.onCopyIndexClick(element.index)}>
Copy
</button>
)}
{element.hasRemove && (
<button className='array-item-remove' onClick={element.onDropIndexClick(element.index)}>
Remove
</button>
)}
<hr />
</div>
))}
{canAdd && (
<div className='array-item-add'>
<button onClick={onAddClick} type='button'>
Add New
</button>
</div>
)}
</div>
);
}
// v6 ArrayFieldTemplate and ArrayFieldItemTemplate (including other v6 changes mentioned in other sections)
import { ArrayFieldItemTemplateProps, ArrayFieldTemplateProps } from '@rjsf/utils';
function CustomArrayFieldItemTemplate(props: ArrayFieldItemTemplateProps) {
const { children, buttonProps, itemKey } = props;
return (
<div className='rjsf-array-item' data-rjsf-itemkey={itemKey}>
<div>{children}</div>
{(buttonsProps.hasMoveUp || buttonsProps.hasMoveDown) && (
<button className='rjsf-array-item-move-down' onClick={props.buttonsProps.onMoveDownItem}>
Down
</button>
)}
{(buttonsProps.hasMoveUp || buttonsProps.hasMoveDown) && (
<button className='rjsf-array-item-move-up' onClick={props.buttonsProps.onMoveUpItem}>
Up
</button>
)}
{buttonsProps.hasCopy && (
<button className='rjsf-array-item-copy' onClick={buttonsProps.onCopyItem}>
Copy
</button>
)}
{buttonsProps.hasRemove && (
<button className='rjsf-array-item-remove' onClick={buttonsProps.onRemoveItem}>
Remove
</button>
)}
<hr />
</div>
);
}
function CustomArrayFieldTemplate(props: ArrayFieldTemplateProps) {
const { items, canAdd, onAddClick } = props;
return (
<div className='array'>
{items}
{canAdd && (
<div className='rjsf-array-item-add'>
<button onClick={onAddClick} type='button'>
Add New
</button>
</div>
)}
</div>
);
}
NOTE: the example above could also have implemented an
ArrayFieldItemButtonsTemplatefor thebuttonPropsrelated code.
Potential BREAKING CHANGE
This template was also updated to support the new Optional Data Controls feature by adding a new optionalDataControl prop to the ArrayFieldTemplateProps interface.
If you have your own implementation of ArrayFieldTemplate and want to support this new feature, please refer to any of the ArrayFieldTemplate implementation for whatever theme you are using and pick up the changes.
ArrayFieldTitleTemplate POTENTIAL BREAKING CHANGE
This template was updated to support the new Optional Data Controls feature by adding a new optionalDataControl prop to the ArrayFieldTitleProps interface.
If you have your own implementation of ArrayFieldTitleTemplate and want to support this new feature, please refer to the ArrayFieldTitleTemplate implementation for @rjsf/core or whatever theme you are using and pick up the changes.
GridTemplate
A new, theme-dependent template GridTemplate was added to support the new layout feature and must be provided if you are building your own registry.templates rather than overloading them via the templates props.
MultiSchemaFieldTemplate
This new template was created to extract styling applied to the MultiSchemaField component in the @rjsf/core package.
If you have styled your form using the following classNames and you do NOT use the @rjsf/core theme, you may need to adjust your styles, as they may have been removed from your theme: panel, panel-default, panel-body, form-group.
ObjectFieldTemplate POTENTIAL BREAKING CHANGE
This template was updated to support the new Optional Data Controls feature by adding a new optionalDataControl prop to the ObjectFieldTemplateProps interface.
If you have your own implementation of ObjectFieldTemplate and want to support this new feature, please refer to the ObjectFieldTemplate implementation for whatever theme you are using and pick up the changes.
OptionalDataControlsTemplate POTENTIAL BREAKING CHANGE
This new template was created to support the new Optional Data Controls feature.
If you are implementing your own theme you may need to add this new template if you anticipate supporting this new feature.
TitleFieldTemplate POTENTIAL BREAKING CHANGE
This template was updated to support the new Optional Data Controls feature by adding a new optionalDataControl prop to the TitleFieldProps interface.
If you have your own implementation of TitleFieldTemplate and want to support this new feature, please refer to the TitleFieldTemplate implementation for whatever theme you are using and pick up the changes.
SchemaField - Addition of FallbackField POTENTIAL BREAKING CHANGE
The SchemaField was updated to use the new FallbackField, which adds opt-in support to replace the UnsupportedField with an interactive UI instead of an error message.
If you have your own implementation of SchemaField and want to support this new feature, please refer to the SchemaField implementation in @rjsf/core and pick up the changes.
The UnsupportedField component is unaffected by these changes, and is still used by the new FallbackField.
SchemaUtilsType
Five new functions were added to this type, so if you have your own implementation of this type, you will need to add them to yours. The following new functions match the 5 new validator-based utility API functions mentioned below:
findFieldInSchema(path: string | string[], schema: S, formData?: T): FoundFieldType<S>findSelectedOptionInXxxOf(schema: S, fallbackField: string, xxx: 'anyOf' |oneOf, formData?: T): S | undefined;getFromSchema(schema: S, path: string | string[], defaultValue: T): T;getFromSchema(schema: S, path: string | string[], defaultValue: S): S;getFromSchema(schema: S, path: string | string[], defaultValue: T | S): S | T;
In addition the toIdSchema() function was removed on this type in concert with the toIdSchema() function removal, so update your implementation to remove this as well.
Removed deprecations
The following deprecations were removed from the code base in v6
FormProps.acceptcharset
The acceptcharset prop on Form was removed. Use the acceptCharset prop instead. I.e.:
<Form acceptCharset='ISO-8859-1' />
getMatchingOption()
The getMatchingOption() function in @rjsf/utils was removed. Use the getFirstMatchingOption() function instead.
I.e.:
import { getFirstMatchingOption, RJSFSchema } from '@rjsf/utils';
import validator from '@rjsf/validator-ajv8';
const schema: RJSFSchema = {
// your schema goes here
};
const formData = {
/* your form data goes here */
};
const options: RJSFSchema[] = [
/* your options extracted from the schema go here */
];
const option = getFirstMatchingOption(validator, formData, options, schema);
SchemaUtilsType.getMatchingOption()
The getMatchingOption() function in the SchemaUtilsType was removed. Use the getFirstMatchingOption() funciton on
the type instead. I.e.:
import { createSchemaUtils, RJSFSchema } from '@rjsf/utils';
import validator from '@rjsf/validator-ajv8';
const schema: RJSFSchema = {
// your schema goes here
};
const formData = {
/* your form data goes here */
};
const options: RJSFSchema[] = [
/* your options extracted from the schema go here */
];
const schemaUtils = createSchemaUtils(validator, schema);
const option = schemaUtils.getFirstMatchingOption(formData, options);
mergeValidationData()
The mergeValidationData() function from @rjsf/utils was removed. Use the validationDataMerge() function instead.
I.e.:
import { validationDataMerge, ValidationData, ErrorSchema } from '@rjsf/utils';
const validationData: ValidationData = {
errors: [
/* Your validation errors go here */
],
errorSchema: {
/* Your error schema goes here */
},
};
const additionalErrorSchema: ErrorSchema = {
/* Your additional error schema goes here */
};
const merged = validationDataMerge(validationData, additionalErrorSchema);
SchemaUtilsType.mergeValidationData()
The mergeValidationData() function in the SchemaUtilsType was removed. Use the standalone validationDataMerge()
function instead. I.e.:
import { toErrorList } from '@rjsf/utils';
const validationData: ValidationData = {
errors: [
/* Your validation errors go here */
],
errorSchema: {
/* Your error schema goes here */
},
};
const additionalErrorSchema: ErrorSchema = {
/* Your additional error schema goes here */
};
const merged = validationDataMerge(validationData, additionalErrorSchema);
ValidatorType.toErrorList()
The toErrorList() function on the ValidatorType interface was removed. Use the standalone toErrorList() function
from @rjsf/utils instead. I.e.:
import { validationDataMerge, ErrorSchema, RJSFValidationError, toErrorList } from '@rjsf/utils';
const errorSchema: ErrorSchema = {
/* Your error schema goes here */
};
const validationErrors: RJSFValidationError[] = toErrorList(errorSchema);
RJSF_ADDITONAL_PROPERTIES_FLAG
The constant RJSF_ADDITONAL_PROPERTIES_FLAG was removed from @rjsf/utils. Use the RJSF_ADDITIONAL_PROPERTIES_FLAG
constant instead. I.e.:
import { RJSF_ADDITIONAL_PROPERTIES_FLAG } from '@rjsf/utils';
UiSchema.classNames
The classNames notation on UiSchema was removed. Use the ui:classNames notation instead. I.e.:
{
"someField": {
"ui:classNames": "someCustomClass"
}
}
schema.enumNames
The custom enumNames property support on a JSON Schema that RJSF invented has been removed. Please use the UiSchema
replacement, ui:enumNames instead. I.e.:
import { UiSchema, RJSFSchema } from '@rjsf/utils';
const schema: RJSFSchema = {
type: 'object',
properties: {
attendance: {
title: 'How did you attend the event?',
enum: ['person', 'phone', 'video'],
},
rating: {
title: 'How would you rate this event?',
enum: ['0', '1', '2', '3', '4'],
},
},
};
const uiSchema: UiSchema = {
attendance: {
'ui:enumNames': ['I joined in person', 'I called in using the phone number', 'I joined using the video link'],
},
rating: {
'ui:enumNames': ["I did't like it", 'It was meh', 'It was ok', 'I liked it', 'I loved it'],
},
};
Use the ui:enumNames in the UiSchema instead.
Other BREAKING CHANGES
Primitive field handling in oneOf/anyOf schemas
A bug fix was implemented that changes how primitive fields (boolean, string, number, etc.) are handled when switching between oneOf/anyOf schema options with mergeDefaultsIntoFormData: "useDefaultIfFormDataUndefined".
Previous (buggy) behavior: Undefined primitive fields were incorrectly set to empty objects {} when switching between schema variants.
New (correct) behavior: Undefined primitive fields now remain undefined or receive proper default values according to their type when switching between schema variants.
This change fixes #4709 and was implemented in #4710.
Impact: If your application was incorrectly relying on undefined primitive fields becoming {} objects, you may need to update your form validation or data processing logic to handle proper primitive values or undefined instead.
SchemaField removed Bootstrap 3 classes
In fixing #2280, the following Bootstrap 3 classes
(form-group, has-error and has-danger error classes) were removed from the classNames prop passed down to the
FieldTemplate. They were instead moved into the core theme's WrapIfAdditionalTemplate to ensure that theme was
unchanged.
Additionally, the Bootstrap 3 classes panel, panel-default, panel-body, and form-group were removed from the
MultiSchemaField component in the @rjsf/core package, and moved into the core theme's MultiSchemaFieldTemplate
to ensure that the theme was unchanged.
As a result, the themes (other than core) will no longer render these classes.
If you use a non-core theme and were relying on them for in your application's styling or behavior (via css overrides
perhaps), then you can still use the non-Bootstrap 3 RJSF marker class (see below) or your specific theme's error classes.
Prefixed RJSF template and field marker classes
Many of the core RJSF templates and field implementations that are shared across all themes were updated to add the
rjsf- prefix to the marker classes that are being added to the rendered HTML. The following table highlights the old
and new marker classes. If you were relying on any of these classes, simply do a rename:
| old marker class | new marker class |
|---|---|
field | rjsf-field |
field-<schema.type> | rjsf-field-<schema.type> |
field-array | rjsf-field-array |
field-array-of-<schema.type> | rjsf-field-array-of-<schema.type> |
field-array-fixed-items | rjsf-field-array-fixed-items |
field-error | rjsf-field-error |
field-hidden | rjsf-field-hidden |
array-item | rjsf-array-item |
config-error | rjsf-config-error |
array-item-add | rjsf-array-item-add |
array-item-copy | rjsf-array-item-copy |
array-item-move-down | rjsf-array-item-move-down |
array-item-move-up | rjsf-array-item-move-up |
array-item-remove | rjsf-array-item-remove |
object-property-expand | rjsf-object-property-expand |
object-property-remove | rjsf-object-property-remove |
optionsList
The generics ordering on the optionsList() function was changed from <S, T, F> to <T, S, F> to be consistent with the rest of the APIs.
New Features
New types
The following new types were added to @rjsf/utils:
ArrayFieldItemTemplateProps: The properties of each element in the ArrayFieldTemplateProps.items array.ContainerFieldTemplateProps: The common properties of the two container templates:ArrayFieldTemplatePropsandObjectFieldTemplatePropsFallbackFieldProps: The properties passed to the newFallbackFieldcomponentFallbackFieldTemplate: The properties passed to the newFallbackFieldTemplatecomponentFieldPathList- The list of path elements that represents where in the schema a field is locatedFieldPathId- Type describing an id and path used for a fieldFoundFieldType: The interface for the return value of thefindFieldInSchemafunctionGlobalFormOptions: The set of options from theFormthat will be available on theRegistryfor use in everywhere theregistryis available.GridTemplateProps: The properties that are passed to aGridTemplateMultiSchemaFieldTemplateProps: The properties that are passed to aMultiSchemaFieldTemplateOptionalDataControlsTemplateProps: The properties that are passed to a OptionalDataControlsTemplate implementationTestIdShape: The interface for the test ID proxy objects that are returned by thegetTestIdutility function
New non-validator utility functions
Many new or formerly internally private utility functions are available in @rjsf/utils:
buttonId(id: FieldPathId | string, btn: 'add' | 'copy' | 'moveDown' | 'moveUp' | 'remove'): Generates consistent ids for RJSF buttonsgetTestIds(): TestIdShape: Returns an object of test IDs that can only be used in test mode, helpful for writing unit tests for React componentshashObject(object: unknown): string: Stringifies anobjectand returns the hash of the resulting stringhashString(string: string): string: Hashes a string into hex formatisFormDataAvailable<T = any>(formData?: T)<T = any>(formData?: T): boolean: Determines whether the givenformDatarepresents valid form data, such as a primitive type, an array, or a non-empty objectisRootSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(registry: Registry<T, S, F>, schemaToCompare: S): boolean: Helper to check whether a JSON schema object is the root schemalookupFromFormContext<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(regOrFc: Registry<T, S, F> | Registry<T, S, F>['formContext'], toLookup: string, fallback?: unknown): Given a React JSON Schema Form registry or formContext object, return the value associated withtoLookupoptionalControlsId(id: FieldPathId | string, element: 'Add' | 'Msg' | 'Remove'): Return a consistentidfor the optional data controlselementshouldRenderOptionalField<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(registry: Registry<T, S, F>, schema: S, required: boolean, uiSchema?: UiSchema<T, S, F>): boolean: Determines if this field should be rendered with the Optional Data Controls UIsortedJSONStringify(object: unknown): string: Stringifies anobject, sorts object fields in consistent order before stringifying ittoFieldPathId(fieldPath: string | number, globalFormOptions: GlobalFormOptions, parentPath?: FieldPathId | FieldPathList, isMultiValue?: boolean): Constructs theFieldPathIdforfieldPathand the optionalparentPath, usingglobalFormOptionsuseAltDateWidgetProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(props: WidgetProps<T, S, F>): UseAltDateWidgetResult: which encapsulates the logic needed to render anAltDateWidgetwith optionaltimeelementsuseDeepCompareMemo<T = unknown>(newValue: T): T: Hook that stores and returns aTvalue. IfnewValueis the same as the stored one, then the stored one is returned, otherwisenewValueis stored and returneduseFileWidgetProps(value: string | string[] | undefined | null, onChange: (value?: string | null | (string | null)[]) => void, multiple?: boolean): UseFileWidgetPropsResult: Hook which encapsulates the logic needed to read and convert avalueofFileorFile[]into the props needed by aFileWidgetimplementation.
New validator-based utility functions
Three new validator-based utility functions are available in @rjsf/utils:
findFieldInSchema<T = undefined, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, rootSchema: S, path: string | string[], schema: S, formData?: T, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): FoundFieldType<S>: Finds the field specified by thepathwithin the root or recursedschemafindSelectedOptionInXxxOf<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, rootSchema: S, schema: S, fallbackField: string,xxx: 'anyOf' | 'oneOf', formData?: T, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): S | undefined: Finds the option that matches the selector field in theschemaor undefined if nothing is selectedgetFromSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, rootSchema: S, schema: S, path: string | string[], defaultValue: T | S, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): T | S: Helper that acts like lodash'sgetbut additionally retrieves$refs as needed to get the path for schemas
Changes to existing utility functions
getDefaultFormState(): Added an optionalinitialDefaultsGeneratedboolean flag that indicates whether or not initial defaults have been generatedretrieveSchema(): Added an optionalresolveAnyOfOrOneOfRefsboolean flag that causes the internalresolveAllSchemas()to resolve$refs inside of the options ofanyOf/oneOfschemas- This new optional flag was added to the
SchemaUtilsTypeinterface's version ofretrieveSchema()as well.
- This new optional flag was added to the
validationDataMerge(): Added optionalpreventDuplicatesboolean flag that causes themergeObjects()call to receivepreventDuplicatesinstead oftrue
initialFormData prop added to Form
A new initialFormData prop was added to Form to support providing initial values for an "uncontrolled" React component.
As a result, a small change was made so that formData now causes the Form to behave as a "controlled" React component.
The programmatic reset() will ensure that an "uncontrolled" form is reset back to the initialFormData values when triggered.
setFieldValue() added to Form
A new programmatic function, setFieldValue(fieldPath: string | FieldPathList, newValue?: T): void was added to the Form.
It allows a user to set a value for the provided fieldPath, which must be either a dotted path to the field OR a FieldPathList.
To set the root element, used either '' or [] for the path. Passing undefined will clear the value in the field.