Skip to main content
Version: Current (v6.0.0-beta.21)

6.x Upgrade Guide

New packages

There are 5 new packages added in RJSF version 6:

  • @rjsf/daisyui: This is new theme based on the daisyui toolkit
  • @rjsf/mantine: This is new theme based on the mantine toolkit
  • @rjsf/primereact: This is a new theme based on the PrimeReact toolkit
  • @rjsf/react-bootstrap: This is rename of the bootstrap-4 theme with an upgrade to the latest version of react-bootstrap
  • @rjsf/shadcn: This is new theme based on the shadcn 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 an ErrorListTemplate implementation
  • FieldProps - The properties that are passed to a Field implementation
  • FieldTemplateProps - The properties that are passed to a FieldTemplate implementation
  • ArrayFieldTemplateProps - The properties that are passed to an ArrayFieldTemplate implementation
  • WidgetProps - The properties that are passed to a Widget 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 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 at index.
  • onCopyIndexClick: (index: number) => (event?: any) => void;: Returns a function that copies the item at index into the position at index + 1.
  • onDropIndexClick: (index) => (event?) => void: Returns a function that removes the item at index.
  • onReorderClick: (index, newIndex) => (event?) => void: Returns a function that swaps the items at index with newIndex.

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 the buttonProps 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 classnew marker class
fieldrjsf-field
field-<schema.type>rjsf-field-<schema.type>
field-arrayrjsf-field-array
field-array-of-<schema.type>rjsf-field-array-of-<schema.type>
field-array-fixed-itemsrjsf-field-array-fixed-items
field-errorrjsf-field-error
field-hiddenrjsf-field-hidden
array-itemrjsf-array-item
config-errorrjsf-config-error
array-item-addrjsf-array-item-add
array-item-copyrjsf-array-item-copy
array-item-move-downrjsf-array-item-move-down
array-item-move-uprjsf-array-item-move-up
array-item-removerjsf-array-item-remove
object-property-expandrjsf-object-property-expand
object-property-removerjsf-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 type
  • ContainerFieldTemplateProps: The common properties of the two container templates: ArrayFieldTemplateProps and ObjectFieldTemplateProps
  • FieldPathList - The list of path elements that represents where in the schema a field is located
  • FieldPathId - Type describing an id and path used for a field
  • FoundFieldType: The interface for the return value of the findFieldInSchema function
  • GlobalFormOptions: The set of options from the Form that will be available on the Registry for use in everywhere the registry is available.
  • GridTemplateProps: The properties that are passed to a GridTemplate
  • MultiSchemaFieldTemplateProps: The properties that are passed to a MultiSchemaFieldTemplate
  • OptionalDataControlsTemplateProps: The properties that are passed to a OptionalDataControlsTemplate implementation
  • TestIdShape: The interface for the test ID proxy objects that are returned by the getTestId 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 buttons
  • getTestIds(): TestIdShape: Returns an object of test IDs that can only be used in test mode, helpful for writing unit tests for React components
  • hashObject(object: unknown): string: Stringifies an object and returns the hash of the resulting string
  • hashString(string: string): string: Hashes a string into hex format
  • isFormDataAvailable<T = any>(formData?: T)<T = any>(formData?: T): boolean: Determines whether the given formData 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 schema
  • lookupFromFormContext<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 with toLookup
  • optionalControlsId(id: FieldPathId | string, element: 'Add' | 'Msg' | 'Remove'): Return a consistent id for the optional data controls element
  • 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 an object, sorts object fields in consistent order before stringifying it.
  • toFieldPathId(fieldPath: string | number, globalFormOptions: GlobalFormOptions, parentPath?: FieldPathId | FieldPathList, isMultiValue?: boolean): Constructs the FieldPathId for fieldPath and the optional parentPath, using globalFormOptions
  • useDeepCompareMemo<T = unknown>(newValue: T): T: Hook that stores and returns a T value. If newValue is the same as the stored one, then the stored one is returned, otherwise newValue 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 the path within the root or recursed schema
  • 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 the schema or undefined if nothing is selected
  • getFromSchema<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's get but additionally retrieves $refs as needed to get the path for schemas

Changes to existing utility functions

  • getDefaultFormState(): Added an optional initialDefaultsGenerated boolean flag that indicates whether or not initial defaults have been generated
  • retrieveSchema(): Added an optional resolveAnyOfOrOneOfRefs boolean flag that causes the internal resolveAllSchemas() to resolve $refs inside of the options of anyOf/oneOf schemas
    • This new optional flag was added to the SchemaUtilsType interface's version of retrieveSchema() as well.
  • validationDataMerge(): Added optional preventDuplicates boolean flag that causes the mergeObjects() call to receive preventDuplicates instead of true

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.