6.x Upgrade Guide
New packages
There are 5 new packages added in RJSF version 6:
@rjsf/daisyui
: This is new theme based on thedaisyui
toolkit@rjsf/mantine
: This is new theme based on themantine
toolkit@rjsf/primereact
: This is a new theme based on thePrimeReact
toolkit@rjsf/react-bootstrap
: This is rename of thebootstrap-4
theme with an upgrade to the latest version ofreact-bootstrap
@rjsf/shadcn
: This is new theme based on theshadcn
toolkit
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 CHANGE
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.
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 anErrorListTemplate
implementationFieldProps
- The properties that are passed to aField
implementationFieldTemplateProps
- The properties that are passed to aFieldTemplate
implementationArrayFieldTemplateProps
- The properties that are passed to anArrayFieldTemplate
implementationWidgetProps
- The properties that are passed to aWidget
implementation
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 Field
s.
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 atindex
into 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 atindex
withnewIndex
.
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
ArrayFieldItemButtonsTemplate
for thebuttonProps
related 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.
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. NOTE:ArrayFieldTemplateItemType
is an alias to this typeContainerFieldTemplateProps
: The common properties of the two container templates:ArrayFieldTemplateProps
andObjectFieldTemplateProps
FieldPathList
- 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 thefindFieldInSchema
functionGlobalFormOptions
: The set of options from theForm
that will be available on theRegistry
for use in everywhere theregistry
is available.GridTemplateProps
: The properties that are passed to aGridTemplate
MultiSchemaFieldTemplateProps
: The properties that are passed to aMultiSchemaFieldTemplate
OptionalDataControlsTemplateProps
: The properties that are passed to a OptionalDataControlsTemplate implementationTestIdShape
: The interface for the test ID proxy objects that are returned by thegetTestId
utility 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 anobject
and 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 givenformData
represents valid form data, such as a primitive type, an array, or a non-empty object.isRootSchema<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 withtoLookup
optionalControlsId(id: FieldPathId | string, element: 'Add' | 'Msg' | 'Remove')
: Return a consistentid
for the optional data controlselement
shouldRenderOptionalField<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 UI.sortedJSONStringify(object: unknown): string
: Stringifies anobject
, sorts object fields in consistent order before stringifying it.toFieldPathId(fieldPath: string | number, globalFormOptions: GlobalFormOptions, parentPath?: FieldPathId | FieldPathList, isMultiValue?: boolean)
: Constructs theFieldPathId
forfieldPath
and the optionalparentPath
, usingglobalFormOptions
useDeepCompareMemo<T = unknown>(newValue: T): T
: Hook that stores and returns aT
value. IfnewValue
is the same as the stored one, then the stored one is returned, otherwisenewValue
is stored and returned
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 thepath
within the root or recursedschema
findSelectedOptionInXxxOf<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 theschema
or 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'sget
but additionally retrieves$ref
s as needed to get the path for schemas
Changes to existing utility functions
getDefaultFormState()
: Added an optionalinitialDefaultsGenerated
boolean flag that indicates whether or not initial defaults have been generatedretrieveSchema()
: Added an optionalresolveAnyOfOrOneOfRefs
boolean flag that causes the internalresolveAllSchemas()
to resolve$ref
s inside of the options ofanyOf
/oneOf
schemas- This new optional flag was added to the
SchemaUtilsType
interface's version ofretrieveSchema()
as well.
- This new optional flag was added to the
validationDataMerge()
: Added optionalpreventDuplicates
boolean flag that causes themergeObjects()
call to receivepreventDuplicates
instead oftrue
Optional Data Controls
RJSF 6.x introduces a new feature that allows developers to provide a condensed UI for users who don't care to enter an optional list of array items or set of optional object fields.
See the documentation for enableOptionalDataFieldForType for more information.
Dynamic UI Schema for Array Items
RJSF 6.x introduces a new feature that allows dynamic UI schema generation for array items.
The items
property in a uiSchema
can now accept a function that returns a UI schema based on the array item's data, index, and form context.
const uiSchema: UiSchema = {
myArrayField: {
items: (itemData, index, formContext) => {
// Return different UI schemas based on item data
if (itemData?.type === 'special') {
return { 'ui:widget': 'textarea' };
}
return { 'ui:widget': 'text' };
},
},
};
This feature is fully backward compatible - existing forms using object-based uiSchema.items
will continue to work without changes.
See the Dynamic UI Schema Examples documentation for comprehensive examples and usage patterns.
Custom field name
generation
RJSF 6.x adds support for customizing how HTML name
attributes are generated for form fields via the new nameGenerator
prop. This enables proper form data submission to backend frameworks that expect specific naming conventions like bracket notation (root[tasks][0][title]
) for PHP/Rails or dot notation (root.tasks.0.title
) for other frameworks.
The default behavior is unchanged if the prop is not provided.